From 10738d36b150aa65206890c1c845cdba076e4200 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 23 Dec 2010 23:51:58 +0000 Subject: [PATCH] Add an AST representation for non-type template parameter packs, e.g., template struct multi_array; along with semantic analysis support for finding unexpanded non-type template parameter packs in types, expressions, and so on. Template instantiation involving non-type template parameter packs probably doesn't work yet. That'll come soon. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122527 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclTemplate.h | 27 ++++++++++++++++--- include/clang/AST/Expr.h | 1 + include/clang/Basic/DiagnosticCommonKinds.td | 2 ++ include/clang/Basic/DiagnosticParseKinds.td | 3 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 -- lib/AST/ASTContext.cpp | 1 + lib/AST/ASTImporter.cpp | 2 +- lib/AST/DeclBase.cpp | 4 ++- lib/AST/DeclTemplate.cpp | 5 ++-- lib/AST/Expr.cpp | 10 +++++-- lib/Sema/SemaTemplate.cpp | 12 +++------ lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 ++- lib/Sema/SemaTemplateVariadic.cpp | 21 ++++++++++++--- lib/Sema/SemaType.cpp | 2 ++ lib/Serialization/ASTReaderDecl.cpp | 3 ++- lib/Serialization/ASTWriterDecl.cpp | 1 + test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp | 4 +-- test/CXX/temp/temp.decls/temp.variadic/p5.cpp | 20 ++++++++++++++ 18 files changed, 93 insertions(+), 30 deletions(-) diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 7a311c8741..f2f91786bc 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -990,17 +990,27 @@ class NonTypeTemplateParmDecl /// it was inherited. llvm::PointerIntPair DefaultArgumentAndInherited; + // FIXME: Collapse this into TemplateParamPosition; or, just move depth/index + // down here to save memory. + + /// \brief Whether this non-type template parameter is a parameter pack. + bool ParameterPack; + NonTypeTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, - TypeSourceInfo *TInfo) + bool ParameterPack, TypeSourceInfo *TInfo) : VarDecl(NonTypeTemplateParm, DC, L, Id, T, TInfo, SC_None, SC_None), - TemplateParmPosition(D, P), DefaultArgumentAndInherited(0, false) + TemplateParmPosition(D, P), DefaultArgumentAndInherited(0, false), + ParameterPack(ParameterPack) { } + friend class ASTDeclReader; + public: static NonTypeTemplateParmDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D, - unsigned P, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo); + unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, + TypeSourceInfo *TInfo); using TemplateParmPosition::getDepth; using TemplateParmPosition::setDepth; @@ -1042,6 +1052,17 @@ public: DefaultArgumentAndInherited.setInt(false); } + /// \brief Whether this parameter is a non-type template parameter pack. + /// + /// If the parameter is a parameter pack, the type may be a + /// \c PackExpansionType. In the following example, the \c Dims parameter + /// is a parameter pack (whose type is 'unsigned'). + /// + /// \code + /// template struct multi_array; + /// \endcode + bool isParameterPack() const { return ParameterPack; } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const NonTypeTemplateParmDecl *D) { return true; } diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index e12f45b49e..d2add78cd8 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -3564,6 +3564,7 @@ class BlockDeclRefExpr : public Expr { Stmt *CopyConstructorVal; public: // FIXME: Fix type/value dependence! + // FIXME: Variadic templates. BlockDeclRefExpr(ValueDecl *d, QualType t, ExprValueKind VK, SourceLocation l, bool ByRef, bool constAdded = false, Stmt *copyConstructorVal = 0) diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td index 98ea9d4bd6..1374402012 100644 --- a/include/clang/Basic/DiagnosticCommonKinds.td +++ b/include/clang/Basic/DiagnosticCommonKinds.td @@ -47,6 +47,8 @@ def err_param_redefinition : Error<"redefinition of parameter %0">; def err_invalid_storage_class_in_func_decl : Error< "invalid storage class specifier in function declarator">; def err_expected_namespace_name : Error<"expected namespace name">; +def err_variadic_templates : Error< + "variadic templates are only allowed in C++0x">; // Sema && Lex def ext_longlong : Extension< diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 3d677abb42..e8eb111795 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -374,9 +374,6 @@ def err_expected_type_name_after_typename : Error< def err_explicit_spec_non_template : Error< "explicit %select{specialization|instantiation}0 of non-template " "%select{class|struct|union}1 %2">; - -def err_variadic_templates : Error< - "variadic templates are only allowed in C++0x">; def err_default_template_template_parameter_not_template : Error< "default template argument for a template template parameter must be a class " diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 8e7a2ba818..49c9c4b6b7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1876,8 +1876,6 @@ def err_pack_expansion_mismatch_unsupported : Error< "clang cannot yet instantiate pack expansions with mismatched pack levels">; def err_function_parameter_pack_unsupported : Error< "clang does not yet support function parameter packs">; -def err_non_type_parameter_pack_unsupported : Error< - "clang does not yet support non-type template parameter packs">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e20b1ed63f..eaf222cafa 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -109,6 +109,7 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( SourceLocation(), NTTP->getDepth(), NTTP->getPosition(), 0, getCanonicalType(NTTP->getType()), + NTTP->isParameterPack(), 0)); else CanonParams.push_back(getCanonicalTemplateTemplateParmDecl( diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 7ff217e334..7d6c90519b 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -3414,7 +3414,7 @@ ASTNodeImporter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { Importer.getToContext().getTranslationUnitDecl(), Loc, D->getDepth(), D->getPosition(), Name.getAsIdentifierInfo(), - T, TInfo); + T, D->isParameterPack(), TInfo); } Decl * diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 843e907dea..75ea2df27e 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -110,7 +110,9 @@ void Decl::add(Kind k) { bool Decl::isTemplateParameterPack() const { if (const TemplateTypeParmDecl *TTP = dyn_cast(this)) return TTP->isParameterPack(); - + if (const NonTypeTemplateParmDecl *NTTP + = llvm::dyn_cast(this)) + return NTTP->isParameterPack(); return false; } diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 422e5e30f8..dff956c6dc 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -390,8 +390,9 @@ NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, - TypeSourceInfo *TInfo) { - return new (C) NonTypeTemplateParmDecl(DC, L, D, P, Id, T, TInfo); + bool ParameterPack, TypeSourceInfo *TInfo) { + return new (C) NonTypeTemplateParmDecl(DC, L, D, P, Id, T, ParameterPack, + TInfo); } SourceLocation NonTypeTemplateParmDecl::getDefaultArgumentLoc() const { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 5d0e0e3093..d8bb0af018 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -208,8 +208,14 @@ void DeclRefExpr::computeDependence() { // member of an unknown specialization. // (handled by DependentScopeDeclRefExpr) - // FIXME: Variadic templates require that we compute whether this - // declaration reference contains an unexpanded parameter pack. + // Determine whether this expression contains any unexpanded parameter + // packs. + // Is the declaration a parameter pack? + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(D)) { + if (NTTP->isParameterPack()) + ExprBits.ContainsUnexpandedParameterPack = true; + } + // FIXME: Variadic templates function parameter packs. } DeclRefExpr::DeclRefExpr(NestedNameSpecifier *Qualifier, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 7357e94490..a487825bb3 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -632,16 +632,12 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, Invalid = true; } - if (D.hasEllipsis()) { - // FIXME: Variadic templates. - Diag(D.getEllipsisLoc(), diag::err_non_type_parameter_pack_unsupported); - Invalid = true; - } - + bool IsParameterPack = D.hasEllipsis(); NonTypeTemplateParmDecl *Param = NonTypeTemplateParmDecl::Create(Context, Context.getTranslationUnitDecl(), D.getIdentifierLoc(), - Depth, Position, ParamName, T, TInfo); + Depth, Position, ParamName, T, + IsParameterPack, TInfo); if (Invalid) Param->setInvalidDecl(); @@ -652,7 +648,7 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, } // Check the well-formedness of the default template argument, if provided. - if (Default) { + if (Default) { // Check for unexpanded parameter packs. if (DiagnoseUnexpandedParameterPack(Default, UPPC_DefaultArgument)) return Param; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 54c509544f..9db3a8cacf 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1506,11 +1506,12 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( Invalid = true; } + // FIXME: Variadic templates. NonTypeTemplateParmDecl *Param = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner, D->getLocation(), D->getDepth() - TemplateArgs.getNumLevels(), D->getPosition(), D->getIdentifier(), T, - DI); + D->isParameterPack(), DI); if (Invalid) Param->setInvalidDecl(); diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index d9c7e72a86..fe30ba5845 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -63,8 +63,21 @@ namespace { return true; } - // FIXME: Record occurrences of non-type and template template - // parameter packs. + /// \brief Record occurrences of (FIXME: function and) non-type template + /// parameter packs in an expression. + bool VisitDeclRefExpr(DeclRefExpr *E) { + if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(E->getDecl())) { + if (NTTP->isParameterPack()) + Unexpanded.push_back(std::make_pair(NTTP, E->getLocation())); + } + + // FIXME: Function parameter packs. + + return true; + } + + // FIXME: Record occurrences of template template parameter packs. // FIXME: Once we have pack expansions in the AST, block their // traversal. @@ -95,8 +108,8 @@ namespace { /// \brief Suppress traversel into types with location information /// that do not contain unexpanded parameter packs. bool TraverseTypeLoc(TypeLoc TL) { - if (!TL.getType().isNull() && TL. - getType()->containsUnexpandedParameterPack()) + if (!TL.getType().isNull() && + TL.getType()->containsUnexpandedParameterPack()) return inherited::TraverseTypeLoc(TL); return true; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index e693c8d4da..46ec0fe109 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1504,6 +1504,8 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // it expands those parameter packs. if (T->containsUnexpandedParameterPack()) T = Context.getPackExpansionType(T); + else if (!getLangOptions().CPlusPlus0x) + Diag(D.getEllipsisLoc(), diag::err_variadic_templates); break; case Declarator::FileContext: diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 6100d65757..f2183ea695 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1156,6 +1156,7 @@ void ASTDeclReader::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { D->setDepth(Record[Idx++]); D->setPosition(Record[Idx++]); // Rest of NonTypeTemplateParmDecl. + D->ParameterPack = Record[Idx++]; if (Record[Idx++]) { Expr *DefArg = Reader.ReadExpr(F); bool Inherited = Record[Idx++]; @@ -1429,7 +1430,7 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) { break; case DECL_NON_TYPE_TEMPLATE_PARM: D = NonTypeTemplateParmDecl::Create(*Context, 0, SourceLocation(), 0,0,0, - QualType(),0); + QualType(), false, 0); break; case DECL_TEMPLATE_TEMPLATE_PARM: D = TemplateTemplateParmDecl::Create(*Context, 0, SourceLocation(),0,0,0,0); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index aa145c415b..add6cd343d 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -994,6 +994,7 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); // Rest of NonTypeTemplateParmDecl. + Record.push_back(D->isParameterPack()); Record.push_back(D->getDefaultArgument() != 0); if (D->getDefaultArgument()) { Writer.AddStmt(D->getDefaultArgument()); diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp index 59a0e7538d..d6df2370ad 100644 --- a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp +++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp @@ -11,12 +11,12 @@ void f1(const Types &...args); // FIXME: temporary expected-error{{clang does no // [ Note: Otherwise, the parameter-declaration is part of a // template-parameter-list and the parameter pack is a template // parameter pack; see 14.1. -- end note ] -template // FIXME: temporary expected-error{{clang does not yet support non-type template parameter packs}} +template struct X0 { }; template struct X1 { - template struct Inner; // FIXME: temporary expected-error{{clang does not yet support non-type template parameter packs}} + template struct Inner; }; // A declarator-id or abstract-declarator containing an ellipsis shall diff --git a/test/CXX/temp/temp.decls/temp.variadic/p5.cpp b/test/CXX/temp/temp.decls/temp.variadic/p5.cpp index aff9b44539..47581fee90 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/p5.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/p5.cpp @@ -128,6 +128,26 @@ struct TestPPName }; // FIXME: Test for unexpanded parameter packs in each of the expression nodes. +template +void test_unexpanded_in_exprs() { + // PredefinedExpr is uninteresting + // DeclRefExpr + Values; // expected-error{{expression contains unexpanded parameter pack 'Values'}} + // IntegerLiteral is uninteresting + // FloatingLiteral is uninteresting + // ImaginaryLiteral is uninteresting + // StringLiteral is uninteresting + // CharacterLiteral is uninteresting + (Values); // expected-error{{expression contains unexpanded parameter pack 'Values'}} + // UnaryOperator + -Values; // expected-error{{expression contains unexpanded parameter pack 'Values'}} + // OffsetOfExpr + struct OffsetMe { + int array[17]; + }; + __builtin_offsetof(OffsetMe, array[Values]); // expected-error{{expression contains unexpanded parameter pack 'Values'}} + // FIXME: continue this... +} template void TestPPNameFunc(int i) {