hamzasood updated this revision to Diff 112018.
hamzasood added a comment.
- Info about a lambda's explicit template parameters is now exposed in the AST
(see changes to LambdaExpr). It turns out that no extra storage was actually
needed to achieve this.
- Removed remnants of a previous implementation (unused
getExplicitTemplateParams function, creation of a TemplateParameterList in
ActOnLambdaTemplateParameterList).
- Renamed ActOnLambdaTemplateParameterList ->
ActOnLambdaExplicitTemplateParameterList (which is a bit more clear).
- Changed LambdaScopeInfo::TemplateParams from a vector of Decl* to a vector of
NamedDecl*.
https://reviews.llvm.org/D36527
Files:
include/clang/AST/ExprCXX.h
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Sema/ScopeInfo.h
include/clang/Sema/Sema.h
lib/AST/ExprCXX.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaLambda.cpp
lib/Sema/SemaType.cpp
test/CXX/temp/temp.decls/temp.variadic/p4.cpp
test/Parser/cxx2a-template-lambdas.cpp
test/SemaCXX/cxx2a-template-lambdas.cpp
www/cxx_status.html
Index: www/cxx_status.html
===================================================================
--- www/cxx_status.html
+++ www/cxx_status.html
@@ -822,7 +822,7 @@
<tr>
<td><i>template-parameter-list</i> for generic lambdas</td>
<td><a href="http://wg21.link/p0428r2">P0428R2</a></td>
- <td class="none" align="center">No</td>
+ <td class="svn" align="center">SVN</td>
</tr>
<tr>
<td>Initializer list constructors in class template argument deduction</td>
Index: test/SemaCXX/cxx2a-template-lambdas.cpp
===================================================================
--- test/SemaCXX/cxx2a-template-lambdas.cpp
+++ test/SemaCXX/cxx2a-template-lambdas.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+template<typename, typename>
+constexpr bool is_same = false;
+
+template<typename T>
+constexpr bool is_same<T, T> = true;
+
+template<typename T>
+struct DummyTemplate { };
+
+void func() {
+ auto L0 = []<typename T>(T arg) {
+ static_assert(is_same<T, int>);
+ };
+ L0(0);
+
+ auto L1 = []<int I> {
+ static_assert(I == 5);
+ };
+ L1.operator()<5>();
+
+ auto L2 = []<template<typename> class T, class U>(T<U> &&arg) {
+ static_assert(is_same<T<U>, DummyTemplate<float>>);
+ };
+ L2(DummyTemplate<float>());
+}
+
+template<typename T> // expected-note {{declared here}}
+struct ShadowMe {
+ void member_func() {
+ auto L = []<typename T> { }; // expected-error {{'T' shadows template parameter}}
+ }
+};
Index: test/Parser/cxx2a-template-lambdas.cpp
===================================================================
--- test/Parser/cxx2a-template-lambdas.cpp
+++ test/Parser/cxx2a-template-lambdas.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -std=c++2a %s -verify
+
+auto L0 = []<> { }; //expected-error {{cannot be empty}}
+
+auto L1 = []<typename T1, typename T2> { };
+auto L2 = []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { };
+auto L3 = []<typename T>(auto arg) { T t; };
+auto L4 = []<int I>() { };
Index: test/CXX/temp/temp.decls/temp.variadic/p4.cpp
===================================================================
--- test/CXX/temp/temp.decls/temp.variadic/p4.cpp
+++ test/CXX/temp/temp.decls/temp.variadic/p4.cpp
@@ -213,8 +213,10 @@
};
#endif
+#if __cplusplus >= 201707L
// - in a template parameter pack that is a pack expansion
- // FIXME: We do not support any way to reach this case yet.
+ swallow([]<T *...v, template<T *> typename ...W>(W<v> ...wv) { });
+#endif
// - in an initializer-list
int arr[] = {T().x...};
@@ -279,11 +281,6 @@
struct T { int x; using U = int; };
void g() { f<T>(1, 2, 3); }
- template<typename ...T, typename ...U> void pack_in_lambda(U ...u) { // expected-note {{here}}
- // FIXME: Move this test into 'f' above once we support this syntax.
- []<T *...v, template<T *> typename ...U>(U<v> ...uv) {}; // expected-error {{expected body of lambda}} expected-error {{does not refer to a value}}
- }
-
template<typename ...T> void pack_expand_attr() {
// FIXME: Move this test into 'f' above once we support this.
[[gnu::aligned(alignof(T))...]] int x; // expected-error {{cannot be used as an attribute pack}} expected-error {{unexpanded}}
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -2790,7 +2790,7 @@
sema::LambdaScopeInfo *LSI = SemaRef.getCurLambda();
assert(LSI && "No LambdaScopeInfo on the stack!");
const unsigned TemplateParameterDepth = LSI->AutoTemplateParameterDepth;
- const unsigned AutoParameterPosition = LSI->AutoTemplateParams.size();
+ const unsigned AutoParameterPosition = LSI->TemplateParams.size();
const bool IsParameterPack = D.hasEllipsis();
// Create the TemplateTypeParmDecl here to retrieve the corresponding
@@ -2802,7 +2802,8 @@
/*KeyLoc*/SourceLocation(), /*NameLoc*/D.getLocStart(),
TemplateParameterDepth, AutoParameterPosition,
/*Identifier*/nullptr, false, IsParameterPack);
- LSI->AutoTemplateParams.push_back(CorrespondingTemplateParam);
+ CorrespondingTemplateParam->setImplicit();
+ LSI->TemplateParams.push_back(CorrespondingTemplateParam);
// Replace the 'auto' in the function parameter with this invented
// template type parameter.
// FIXME: Retain some type sugar to indicate that this was written
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp
+++ lib/Sema/SemaLambda.cpp
@@ -21,6 +21,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaLambda.h"
+#include "llvm/ADT/STLExtras.h"
using namespace clang;
using namespace sema;
@@ -226,19 +227,14 @@
static inline TemplateParameterList *
getGenericLambdaTemplateParameterList(LambdaScopeInfo *LSI, Sema &SemaRef) {
- if (LSI->GLTemplateParameterList)
- return LSI->GLTemplateParameterList;
-
- if (!LSI->AutoTemplateParams.empty()) {
- SourceRange IntroRange = LSI->IntroducerRange;
- SourceLocation LAngleLoc = IntroRange.getBegin();
- SourceLocation RAngleLoc = IntroRange.getEnd();
+ if (!LSI->GLTemplateParameterList && !LSI->TemplateParams.empty()) {
LSI->GLTemplateParameterList = TemplateParameterList::Create(
SemaRef.Context,
- /*Template kw loc*/ SourceLocation(), LAngleLoc,
- llvm::makeArrayRef((NamedDecl *const *)LSI->AutoTemplateParams.data(),
- LSI->AutoTemplateParams.size()),
- RAngleLoc, nullptr);
+ /*Template kw loc*/ SourceLocation(),
+ /*L angle loc*/ LSI->ExplicitTemplateParamsRange.getBegin(),
+ LSI->TemplateParams,
+ /*R angle loc*/LSI->ExplicitTemplateParamsRange.getEnd(),
+ nullptr);
}
return LSI->GLTemplateParameterList;
}
@@ -479,6 +475,28 @@
LSI->finishedExplicitCaptures();
}
+void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
+ ArrayRef<Decl *> TParams,
+ SourceLocation RAngleLoc) {
+ LambdaScopeInfo *LSI = getCurLambda();
+ assert(LSI && "Expected a lambda scope");
+
+ assert(LSI->NumExplicitTemplateParams == 0
+ && "Already acted on explicit template parameters");
+ assert(LSI->TemplateParams.size() == 0
+ && "Explicit template parameters should come "
+ "before invented (auto) ones");
+
+ assert(llvm::all_of(TParams, [](Decl *D){ return llvm::isa<NamedDecl>(D); })
+ && "Template params should be convertable to NamedDecl");
+
+ LSI->TemplateParams.append(
+ reinterpret_cast<NamedDecl *const *>(TParams.begin()),
+ reinterpret_cast<NamedDecl *const *>(TParams.end()));
+ LSI->NumExplicitTemplateParams = TParams.size();
+ LSI->ExplicitTemplateParamsRange = {LAngleLoc, RAngleLoc};
+}
+
void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) {
// Introduce our parameters into the function scope
for (unsigned p = 0, NumParams = CallOperator->getNumParams();
@@ -811,17 +829,23 @@
void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
Declarator &ParamInfo,
Scope *CurScope) {
- // Determine if we're within a context where we know that the lambda will
- // be dependent, because there are template parameters in scope.
- bool KnownDependent = false;
LambdaScopeInfo *const LSI = getCurLambda();
assert(LSI && "LambdaScopeInfo should be on stack!");
- // The lambda-expression's closure type might be dependent even if its
- // semantic context isn't, if it appears within a default argument of a
- // function template.
- if (CurScope->getTemplateParamParent())
- KnownDependent = true;
+ // Determine if we're within a context where we know that the lambda will
+ // be dependent, because there are template parameters in scope.
+ bool KnownDependent;
+ if (LSI->NumExplicitTemplateParams > 0) {
+ auto *TemplateParamScope = CurScope->getTemplateParamParent();
+ assert(TemplateParamScope
+ && "Lambda with explicit template param list should establish a "
+ "template param scope");
+ KnownDependent = TemplateParamScope->getParent()
+ ->getTemplateParamParent() != nullptr;
+ }
+ else {
+ KnownDependent = CurScope->getTemplateParamParent() != nullptr;
+ }
// Determine the signature of the call operator.
TypeSourceInfo *MethodTyInfo;
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -1376,7 +1376,7 @@
// an associated template parameter list.
LambdaScopeInfo *Sema::getCurGenericLambda() {
if (LambdaScopeInfo *LSI = getCurLambda()) {
- return (LSI->AutoTemplateParams.size() ||
+ return (LSI->TemplateParams.size() ||
LSI->GLTemplateParameterList) ? LSI : nullptr;
}
return nullptr;
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -635,6 +635,8 @@
///
/// lambda-expression:
/// lambda-introducer lambda-declarator[opt] compound-statement
+/// lambda-introducer <template-parameter-list> lambda-declarator[opt]
+/// compound-statement
///
/// lambda-introducer:
/// '[' lambda-capture[opt] ']'
@@ -1105,6 +1107,30 @@
<< A->getName()->getName();
};
+ // FIXME: Consider allowing this as an extension for GCC compatibiblity.
+ const bool HasExplicitTemplateParams = getLangOpts().CPlusPlus2a
+ && Tok.is(tok::less);
+ ParseScope TemplateParamScope(this, Scope::TemplateParamScope,
+ /*EnteredScope=*/HasExplicitTemplateParams);
+ if (HasExplicitTemplateParams) {
+ SmallVector<Decl*, 4> TemplateParams;
+ SourceLocation LAngleLoc, RAngleLoc;
+ if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
+ TemplateParams, LAngleLoc, RAngleLoc)) {
+ return ExprError();
+ }
+
+ if (TemplateParams.empty()) {
+ Diag(RAngleLoc,
+ diag::err_lambda_template_parameter_list_empty);
+ }
+ else {
+ Actions.ActOnLambdaExplicitTemplateParameterList(
+ LAngleLoc, TemplateParams, RAngleLoc);
+ ++CurTemplateDepthTracker;
+ }
+ }
+
TypeResult TrailingReturnType;
if (Tok.is(tok::l_paren)) {
ParseScope PrototypeScope(this,
@@ -1119,15 +1145,25 @@
// Parse parameter-declaration-clause.
SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo;
SourceLocation EllipsisLoc;
-
+
if (Tok.isNot(tok::r_paren)) {
- Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth);
+ // Record the template parameter depth for auto parameters.
+ // Subtract 1 from the current depth if we've parsed an explicit template
+ // parameter list, because that will have increased the depth.
+ Actions.RecordParsingTemplateParameterDepth(HasExplicitTemplateParams
+ ? TemplateParameterDepth - 1
+ : TemplateParameterDepth);
+
ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
+
// For a generic lambda, each 'auto' within the parameter declaration
- // clause creates a template type parameter, so increment the depth.
- if (Actions.getCurGenericLambda())
+ // clause creates a template type parameter, so increment the depth
+ // (unless we've parsed an explicit template parameter list, in which
+ // case the depth will have already been increased).
+ if (!HasExplicitTemplateParams && Actions.getCurGenericLambda())
++CurTemplateDepthTracker;
}
+
T.consumeClose();
SourceLocation RParenLoc = T.getCloseLocation();
SourceLocation DeclEndLoc = RParenLoc;
@@ -1145,7 +1181,7 @@
SourceLocation ConstexprLoc;
tryConsumeMutableOrConstexprToken(*this, MutableLoc, ConstexprLoc,
DeclEndLoc);
-
+
addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS);
// Parse exception-specification[opt].
@@ -1296,6 +1332,7 @@
StmtResult Stmt(ParseCompoundStatementBody());
BodyScope.Exit();
+ TemplateParamScope.Exit();
if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid())
return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope());
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/IdentifierTable.h"
+#include "llvm/ADT/STLExtras.h"
using namespace clang;
@@ -975,6 +976,13 @@
return capture_range(implicit_capture_begin(), implicit_capture_end());
}
+SourceRange LambdaExpr::getExplicitTemplateParameterListRange() const {
+ TemplateParameterList *List = getTemplateParameterList();
+ if (!List)
+ return SourceRange();
+ return {List->getLAngleLoc(), List->getRAngleLoc()};
+}
+
CXXRecordDecl *LambdaExpr::getLambdaClass() const {
return getType()->getAsCXXRecordDecl();
}
@@ -987,7 +995,13 @@
TemplateParameterList *LambdaExpr::getTemplateParameterList() const {
CXXRecordDecl *Record = getLambdaClass();
return Record->getGenericLambdaTemplateParameterList();
+}
+unsigned LambdaExpr::getExplicitTemplateParameterCount() const {
+ TemplateParameterList *List = getTemplateParameterList();
+ if (!List)
+ return 0;
+ return llvm::count_if(*List, [](NamedDecl *D) { return !D->isImplicit(); });
}
CompoundStmt *LambdaExpr::getBody() const {
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5466,6 +5466,12 @@
/// given lambda.
void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI);
+ /// \brief This is called after parsing the explicit template parameter list
+ /// (if it exists) in C++2a.
+ void ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
+ ArrayRef<Decl *> TParams,
+ SourceLocation RAngleLoc);
+
/// \brief Introduce the lambda parameters into scope.
void addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope);
Index: include/clang/Sema/ScopeInfo.h
===================================================================
--- include/clang/Sema/ScopeInfo.h
+++ include/clang/Sema/ScopeInfo.h
@@ -769,19 +769,28 @@
/// \brief If this is a generic lambda, use this as the depth of
/// each 'auto' parameter, during initial AST construction.
unsigned AutoTemplateParameterDepth;
-
- /// \brief Store the list of the auto parameters for a generic lambda.
- /// If this is a generic lambda, store the list of the auto
- /// parameters converted into TemplateTypeParmDecls into a vector
- /// that can be used to construct the generic lambda's template
+
+ /// \brief The number of parameters in the template parameter list that were
+ /// explicitly specified by the user, as opposed to being invented by use
+ /// of an auto parameter.
+ unsigned NumExplicitTemplateParams;
+
+ /// \brief Source range covering the explicit template parameter list
+ /// (if it exists).
+ SourceRange ExplicitTemplateParamsRange;
+
+ /// \brief Store the list of the template parameters for a generic lambda.
+ /// If this is a generic lambda, this holds the explicit template parameters
+ /// followed by the auto parameters converted into TemplateTypeParmDecls.
+ /// It can be used to construct the generic lambda's template
/// parameter list, during initial AST construction.
- SmallVector<TemplateTypeParmDecl*, 4> AutoTemplateParams;
+ SmallVector<NamedDecl*, 4> TemplateParams;
/// If this is a generic lambda, and the template parameter
- /// list has been created (from the AutoTemplateParams) then
+ /// list has been created (from the TemplateParams) then
/// store a reference to it (cache it to avoid reconstructing it).
TemplateParameterList *GLTemplateParameterList;
-
+
/// \brief Contains all variable-referring-expressions (i.e. DeclRefExprs
/// or MemberExprs) that refer to local variables in a generic lambda
/// or a lambda in a potentially-evaluated-if-used context.
@@ -795,7 +804,6 @@
/// will truly be odr-used (i.e. need to be captured) by that nested lambda,
/// until its instantiation. But we still need to capture it in the
/// enclosing lambda if all intervening lambdas can capture the variable.
-
llvm::SmallVector<Expr*, 4> PotentiallyCapturingExprs;
/// \brief Contains all variable-referring-expressions that refer
@@ -820,7 +828,7 @@
CallOperator(nullptr), NumExplicitCaptures(0), Mutable(false),
ExplicitParams(false), Cleanup{},
ContainsUnexpandedParameterPack(false), AutoTemplateParameterDepth(0),
- GLTemplateParameterList(nullptr) {
+ NumExplicitTemplateParams(0), GLTemplateParameterList(nullptr) {
Kind = SK_Lambda;
}
@@ -834,9 +842,9 @@
}
/// Is this scope known to be for a generic lambda? (This will be false until
- /// we parse the first 'auto'-typed parameter.
+ /// we parse a template parameter list or the first 'auto'-typed parameter).
bool isGenericLambda() const {
- return !AutoTemplateParams.empty() || GLTemplateParameterList;
+ return !TemplateParams.empty() || GLTemplateParameterList;
}
///
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -822,6 +822,10 @@
def ext_constexpr_on_lambda_cxx17 : ExtWarn<
"'constexpr' on lambda expressions is a C++17 extension">, InGroup<CXX17>;
+ // C++2a template lambdas
+ def err_lambda_template_parameter_list_empty : Error<
+ "lambda template parameter list cannot be empty">;
+
// Availability attribute
def err_expected_version : Error<
"expected a version of the form 'major[.minor[.subminor]]'">;
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -1684,6 +1684,10 @@
/// brackets ([...]).
SourceRange getIntroducerRange() const { return IntroducerRange; }
+ /// \brief Retrieve the source range covering the explicit template parameter
+ /// list, including the surrounding angle brackets (<...>).
+ SourceRange getExplicitTemplateParameterListRange() const;
+
/// \brief Retrieve the class that corresponds to the lambda.
///
/// This is the "closure type" (C++1y [expr.prim.lambda]), and stores the
@@ -1699,6 +1703,10 @@
/// parameter list associated with it, or else return null.
TemplateParameterList *getTemplateParameterList() const;
+ /// \brief How many of the template parameters were explicitly specified
+ /// (as opposed to being invented by use of an auto parameter).
+ unsigned getExplicitTemplateParameterCount() const;
+
/// \brief Whether this is a generic lambda.
bool isGenericLambda() const { return getTemplateParameterList(); }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits