41 changes: 39 additions & 2 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,8 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
///
/// lambda-expression:
/// lambda-introducer lambda-declarator[opt] compound-statement
/// lambda-introducer '<' template-parameter-list '>'
/// lambda-declarator[opt] compound-statement
///
/// lambda-introducer:
/// '[' lambda-capture[opt] ']'
Expand Down Expand Up @@ -1121,6 +1123,33 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
<< A.getName()->getName();
};

// FIXME: Consider allowing this as an extension for GCC compatibiblity.
const bool HasExplicitTemplateParams = Tok.is(tok::less);
ParseScope TemplateParamScope(this, Scope::TemplateParamScope,
/*EnteredScope=*/HasExplicitTemplateParams);
if (HasExplicitTemplateParams) {
Diag(Tok, getLangOpts().CPlusPlus2a
? diag::warn_cxx17_compat_lambda_template_parameter_list
: diag::ext_lambda_template_parameter_list);

SmallVector<NamedDecl*, 4> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
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,
Expand All @@ -1137,13 +1166,20 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
SourceLocation EllipsisLoc;

if (Tok.isNot(tok::r_paren)) {
Actions.RecordParsingTemplateParameterDepth(TemplateParameterDepth);
Actions.RecordParsingTemplateParameterDepth(
CurTemplateDepthTracker.getOriginalDepth());

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 we've parsed any explicit template parameters, then the depth will
// have already been incremented. So we make sure that at most a single
// depth level is added.
if (Actions.getCurGenericLambda())
++CurTemplateDepthTracker;
CurTemplateDepthTracker.setAddedDepth(1);
}

T.consumeClose();
SourceLocation RParenLoc = T.getCloseLocation();
SourceLocation DeclEndLoc = RParenLoc;
Expand Down Expand Up @@ -1298,6 +1334,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(

StmtResult Stmt(ParseCompoundStatementBody());
BodyScope.Exit();
TemplateParamScope.Exit();

if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid())
return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get(), getCurScope());
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1793,7 +1793,7 @@ LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) {
// 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;
Expand Down
57 changes: 38 additions & 19 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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;

Expand Down Expand Up @@ -225,19 +226,14 @@ Optional<unsigned> clang::getStackIndexOfNearestEnclosingCaptureCapableLambda(

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;
}
Expand Down Expand Up @@ -492,6 +488,23 @@ void Sema::finishLambdaExplicitCaptures(LambdaScopeInfo *LSI) {
LSI->finishedExplicitCaptures();
}

void Sema::ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> 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.empty() &&
"Explicit template parameters should come "
"before invented (auto) ones");
assert(!TParams.empty() &&
"No template parameters to act on");
LSI->TemplateParams.append(TParams.begin(), TParams.end());
LSI->NumExplicitTemplateParams = TParams.size();
LSI->ExplicitTemplateParamsRange = {LAngleLoc, RAngleLoc};
}

void Sema::addLambdaParameters(
ArrayRef<LambdaIntroducer::LambdaCapture> Captures,
CXXMethodDecl *CallOperator, Scope *CurScope) {
Expand Down Expand Up @@ -832,17 +845,23 @@ FieldDecl *Sema::buildInitCaptureField(LambdaScopeInfo *LSI, VarDecl *Var) {
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");
assert(TemplateParamScope->getParent());
KnownDependent = TemplateParamScope->getParent()
->getTemplateParamParent() != nullptr;
} else {
KnownDependent = CurScope->getTemplateParamParent() != nullptr;
}

// Determine the signature of the call operator.
TypeSourceInfo *MethodTyInfo;
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2935,7 +2935,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
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
Expand All @@ -2947,7 +2947,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
/*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(),
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
Expand Down
9 changes: 3 additions & 6 deletions clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,10 @@ namespace PackExpansionWithinLambda {
};
#endif

#if __cplusplus > 201703L
// - 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...};
Expand Down Expand Up @@ -279,11 +281,6 @@ namespace PackExpansionWithinLambda {
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}}
Expand Down
34 changes: 34 additions & 0 deletions clang/test/CodeGenCXX/mangle-lambda-explicit-template-params.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %clang_cc1 -std=c++2a -triple %itanium_abi_triple -emit-llvm -o - %s -w | FileCheck %s

template<class, int, class>
struct DummyType { };

inline void inline_func() {
// CHECK: UlvE
[]{}();

// CHECK: UlTyvE
[]<class>{}.operator()<int>();

// CHECK: UlTyT_E
[]<class T>(T){}(1);

// CHECK: UlTyTyT_T0_E
[]<class T1, class T2>(T1, T2){}(1, 2);

// CHECK: UlTyTyT0_T_E
[]<class T1, class T2>(T2, T1){}(2, 1);

// CHECK: UlTniTyTnjT0_E
[]<int I, class T, unsigned U>(T){}.operator()<1, int, 2>(3);

// CHECK: UlTyTtTyTniTyETniTyvE
[]<class,
template<class, int, class> class,
int,
class>{}.operator()<unsigned, DummyType, 5, int>();
}

void call_inline_func() {
inline_func();
}
2 changes: 1 addition & 1 deletion clang/test/Index/print-display-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ template<> void g<int>(ClassTmpl<int, int>);
// DISPLAY_NAME: print-display-names.cpp:13:17: FunctionDecl=g<>(ClassTmpl<int, int>):13:17 [Specialization of g:11:6]

// RUN: env CINDEXTEST_PRINTINGPOLICY_TERSEOUTPUT=1 c-index-test -test-load-source all-pretty %s | FileCheck %s --check-prefix=PRETTY
// PRETTY: print-display-names.cpp:2:7: ClassTemplate=template <typename T, typename > class ClassTmpl {}:2:7 (Definition) Extent=[1:1 - 2:20]
// PRETTY: print-display-names.cpp:2:7: ClassTemplate=template <typename T, typename> class ClassTmpl {}:2:7 (Definition) Extent=[1:1 - 2:20]
// PRETTY: print-display-names.cpp:4:13: TypedefDecl=typedef int Integer:4:13 (Definition) Extent=[4:1 - 4:20]
// PRETTY: print-display-names.cpp:6:16: ClassDecl=template<> class ClassTmpl<int, int> {}:6:16 (Definition) [Specialization of ClassTmpl:2:7] Extent=[6:1 - 6:43]
// PRETTY: print-display-names.cpp:8:6: FunctionDecl=void f(ClassTmpl<float, Integer> p):8:6 Extent=[8:1 - 8:36]
Expand Down
2 changes: 1 addition & 1 deletion clang/test/PCH/cxx11-lambdas.mm
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ int add(int x, int y) {
}

// CHECK-PRINT: inline int add_int_slowly_twice
// CHECK-PRINT: lambda = [&] (int z)
// CHECK-PRINT: lambda = [&](int z)

// CHECK-PRINT: init_capture
// CHECK-PRINT: [&, x(t)]
Expand Down
2 changes: 1 addition & 1 deletion clang/test/PCH/cxx1y-lambdas.mm
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ int add(int x, int y) {
}

// CHECK-PRINT: inline int add_int_slowly_twice
// CHECK-PRINT: lambda = [] (type-parameter-0-0 z
// CHECK-PRINT: lambda = [](auto z

// CHECK-PRINT: init_capture
// CHECK-PRINT: [&, x(t)]
Expand Down
42 changes: 42 additions & 0 deletions clang/test/PCH/cxx2a-template-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t
// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s

// expected-no-diagnostics

#ifndef HEADER
#define HEADER

auto l1 = []<int I>() constexpr -> int {
return I;
};

auto l2 = []<auto I>() constexpr -> decltype(I) {
return I;
};

auto l3 = []<class T>(auto i) constexpr -> T {
return T(i);
};

auto l4 = []<template<class> class T, class U>(T<U>, auto i) constexpr -> U {
return U(i);
};

#else /*included pch*/

static_assert(l1.operator()<5>() == 5);
static_assert(l1.operator()<6>() == 6);

static_assert(l2.operator()<7>() == 7);
static_assert(l2.operator()<nullptr>() == nullptr);

static_assert(l3.operator()<int>(8.4) == 8);
static_assert(l3.operator()<int>(9.9) == 9);

template<typename T>
struct DummyTemplate { };

static_assert(l4(DummyTemplate<float>(), 12) == 12.0);
static_assert(l4(DummyTemplate<int>(), 19.8) == 19);

#endif // HEADER
8 changes: 8 additions & 0 deletions clang/test/Parser/cxx2a-template-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -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>() { };
45 changes: 45 additions & 0 deletions clang/test/SemaCXX/cxx2a-template-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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>); // expected-error {{static_assert failed}}
};
L0(0);
L0(0.0); // expected-note {{in instantiation}}

auto L1 = []<int I> {
static_assert(I == 5); // expected-error {{static_assert failed}}
};
L1.operator()<5>();
L1.operator()<6>(); // expected-note {{in instantiation}}

auto L2 = []<template<typename> class T, class U>(T<U> &&arg) {
static_assert(is_same<T<U>, DummyTemplate<float>>); // // expected-error {{static_assert failed}}
};
L2(DummyTemplate<float>());
L2(DummyTemplate<double>()); // expected-note {{in instantiation}}
}

template<typename T> // expected-note {{declared here}}
struct ShadowMe {
void member_func() {
auto L = []<typename T> { }; // expected-error {{'T' shadows template parameter}}
}
};

template<typename T>
constexpr T outer() {
return []<T x>() { return x; }.template operator()<123>(); // expected-error {{no matching member function}} \
expected-note {{candidate template ignored}}
}
static_assert(outer<int>() == 123);
template int *outer<int *>(); // expected-note {{in instantiation}}
37 changes: 37 additions & 0 deletions clang/unittests/AST/StmtPrinterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,43 @@ TEST(StmtPrinter, TestCXXConversionDeclExplicit) {
// WRONG; Should be: (a & b).operator void *()
}

TEST(StmtPrinter, TestCXXLamda) {
ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
"void A() {"
" auto l = [] { };"
"}",
lambdaExpr(anything()).bind("id"),
"[] {\n"
"}"));

ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
"void A() {"
" int a = 0, b = 1;"
" auto l = [a,b](int c, float d) { };"
"}",
lambdaExpr(anything()).bind("id"),
"[a, b](int c, float d) {\n"
"}"));

ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14,
"void A() {"
" auto l = [](auto a, int b, auto c, int, auto) { };"
"}",
lambdaExpr(anything()).bind("id"),
"[](auto a, int b, auto c, int, auto) {\n"
"}"));

ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX2a,
"void A() {"
" auto l = []<typename T1, class T2, int I,"
" template<class, typename> class T3>"
" (int a, auto, int, auto d) { };"
"}",
lambdaExpr(anything()).bind("id"),
"[]<typename T1, class T2, int I, template <class, typename> class T3>(int a, auto, int, auto d) {\n"
"}"));
}

TEST(StmtPrinter, TestNoImplicitBases) {
const char *CPPSource = R"(
class A {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===- unittest/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "TestVisitor.h"

using namespace clang;

namespace {

// Matches (optional) explicit template parameters.
class LambdaTemplateParametersVisitor
: public ExpectedLocationVisitor<LambdaTemplateParametersVisitor> {
public:
bool shouldVisitImplicitCode() const { return false; }

bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
EXPECT_FALSE(D->isImplicit());
Match(D->getName(), D->getLocStart());
return true;
}

bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) {
EXPECT_FALSE(D->isImplicit());
Match(D->getName(), D->getLocStart());
return true;
}

bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
EXPECT_FALSE(D->isImplicit());
Match(D->getName(), D->getLocStart());
return true;
}
};

TEST(RecursiveASTVisitor, VisitsLambdaExplicitTemplateParameters) {
LambdaTemplateParametersVisitor Visitor;
Visitor.ExpectMatch("T", 2, 15);
Visitor.ExpectMatch("I", 2, 24);
Visitor.ExpectMatch("TT", 2, 31);
EXPECT_TRUE(Visitor.runOver(
"void f() { \n"
" auto l = []<class T, int I, template<class> class TT>(auto p) { }; \n"
"}",
LambdaTemplateParametersVisitor::Lang_CXX2a));
}

} // end anonymous namespace
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ <h2 id="cxx20">C++2a implementation status</h2>
<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 id="p0734">
<td rowspan="4">Concepts</td>
Expand Down