Skip to content

Commit

Permalink
[c++20] Implement P0428R2 - Familiar template syntax for generic lambdas
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D36527

llvm-svn: 359967
  • Loading branch information
hamzasood committed May 4, 2019
1 parent 9c32fa1 commit 8205a81
Show file tree
Hide file tree
Showing 29 changed files with 464 additions and 61 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Expand Up @@ -1221,6 +1221,9 @@ class CXXRecordDecl : public RecordDecl {
/// lambda.
TemplateParameterList *getGenericLambdaTemplateParameterList() const;

/// Retrieve the lambda template parameters that were specified explicitly.
ArrayRef<NamedDecl *> getLambdaExplicitTemplateParameters() const;

LambdaCaptureDefault getLambdaCaptureDefault() const {
assert(isLambda());
return static_cast<LambdaCaptureDefault>(getLambdaData().CaptureDefault);
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Expand Up @@ -176,6 +176,11 @@ class TemplateParameterList final
return SourceRange(TemplateLoc, RAngleLoc);
}

void print(raw_ostream &Out, const ASTContext &Context,
bool OmitTemplateKW = false) const;
void print(raw_ostream &Out, const ASTContext &Context,
const PrintingPolicy &Policy, bool OmitTemplateKW = false) const;

public:
// FIXME: workaround for MSVC 2013; remove when no longer needed
using FixedSizeStorageOwner = TrailingObjects::FixedSizeStorageOwner;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Expand Up @@ -1899,6 +1899,10 @@ class LambdaExpr final : public Expr,
/// parameter list associated with it, or else return null.
TemplateParameterList *getTemplateParameterList() const;

/// Get the template parameters were explicitly specified (as opposed to being
/// invented by use of an auto parameter).
ArrayRef<NamedDecl *> getExplicitTemplateParameters() const;

/// Whether this is a generic lambda.
bool isGenericLambda() const { return getTemplateParameterList(); }

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Expand Up @@ -2423,6 +2423,10 @@ DEF_TRAVERSE_STMT(LambdaExpr, {
TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
FunctionProtoTypeLoc Proto = TL.getAsAdjusted<FunctionProtoTypeLoc>();

for (Decl *D : S->getExplicitTemplateParameters()) {
// Visit explicit template parameters.
TRY_TO(TraverseDecl(D));
}
if (S->hasExplicitParameters()) {
// Visit parameters.
for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I)
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Expand Up @@ -886,6 +886,16 @@ def warn_cxx14_compat_constexpr_on_lambda : Warning<
def ext_constexpr_on_lambda_cxx17 : ExtWarn<
"'constexpr' on lambda expressions is a C++17 extension">, InGroup<CXX17>;

// C++2a template lambdas
def ext_lambda_template_parameter_list: ExtWarn<
"explicit template parameter list for lambdas is a C++2a extension">,
InGroup<CXX2a>;
def warn_cxx17_compat_lambda_template_parameter_list: Warning<
"explicit template parameter list for lambdas is incompatible with "
"C++ standards before C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
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]]'">;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Parse/Parser.h
Expand Up @@ -250,7 +250,13 @@ class Parser : public CodeCompletionHandler {
Depth += D;
AddedLevels += D;
}
void setAddedDepth(unsigned D) {
Depth = Depth - AddedLevels + D;
AddedLevels = D;
}

unsigned getDepth() const { return Depth; }
unsigned getOriginalDepth() const { return Depth - AddedLevels; }
};

/// Factory object for creating ParsedAttr objects.
Expand Down
28 changes: 18 additions & 10 deletions clang/include/clang/Sema/ScopeInfo.h
Expand Up @@ -816,16 +816,24 @@ class LambdaScopeInfo final : public CapturingScopeInfo {
/// each 'auto' parameter, during initial AST construction.
unsigned AutoTemplateParameterDepth = 0;

/// 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
/// parameter list, during initial AST construction.
SmallVector<TemplateTypeParmDecl*, 4> AutoTemplateParams;
/// 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 = 0;

/// Source range covering the explicit template parameter list (if it exists).
SourceRange ExplicitTemplateParamsRange;

/// 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<NamedDecl*, 4> TemplateParams;

/// If this is a generic lambda, and the template parameter
/// list has been created (from the AutoTemplateParams) then
/// store a reference to it (cache it to avoid reconstructing it).
/// list has been created (from the TemplateParams) then store
/// a reference to it (cache it to avoid reconstructing it).
TemplateParameterList *GLTemplateParameterList = nullptr;

/// Contains all variable-referring-expressions (i.e. DeclRefExprs
Expand Down Expand Up @@ -878,9 +886,9 @@ class LambdaScopeInfo final : public CapturingScopeInfo {
}

/// 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;
}

/// Add a variable that might potentially be captured by the
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -5723,6 +5723,12 @@ class Sema {
/// given lambda.
void finishLambdaExplicitCaptures(sema::LambdaScopeInfo *LSI);

/// \brief This is called after parsing the explicit template parameter list
/// on a lambda (if it exists) in C++2a.
void ActOnLambdaExplicitTemplateParameterList(SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> TParams,
SourceLocation RAngleLoc);

/// Introduce the lambda parameters into scope.
void addLambdaParameters(
ArrayRef<LambdaIntroducer::LambdaCapture> Captures,
Expand Down
19 changes: 18 additions & 1 deletion clang/lib/AST/DeclCXX.cpp
Expand Up @@ -1421,13 +1421,30 @@ void CXXRecordDecl::getCaptureFields(

TemplateParameterList *
CXXRecordDecl::getGenericLambdaTemplateParameterList() const {
if (!isLambda()) return nullptr;
if (!isGenericLambda()) return nullptr;
CXXMethodDecl *CallOp = getLambdaCallOperator();
if (FunctionTemplateDecl *Tmpl = CallOp->getDescribedFunctionTemplate())
return Tmpl->getTemplateParameters();
return nullptr;
}

ArrayRef<NamedDecl *>
CXXRecordDecl::getLambdaExplicitTemplateParameters() const {
TemplateParameterList *List = getGenericLambdaTemplateParameterList();
if (!List)
return {};

assert(std::is_partitioned(List->begin(), List->end(),
[](const NamedDecl *D) { return !D->isImplicit(); })
&& "Explicit template params should be ordered before implicit ones");

const auto ExplicitEnd = std::lower_bound(List->begin(), List->end(), false,
[](const NamedDecl *D, bool) {
return !D->isImplicit();
});
return llvm::makeArrayRef(List->begin(), ExplicitEnd);
}

Decl *CXXRecordDecl::getLambdaContextDecl() const {
assert(isLambda() && "Not a lambda closure type!");
ExternalASTSource *Source = getParentASTContext().getExternalSource();
Expand Down
46 changes: 36 additions & 10 deletions clang/lib/AST/DeclPrinter.cpp
Expand Up @@ -15,6 +15,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
Expand Down Expand Up @@ -105,7 +106,8 @@ namespace {
void VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D);
void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D);

void printTemplateParameters(const TemplateParameterList *Params);
void printTemplateParameters(const TemplateParameterList *Params,
bool OmitTemplateKW = false);
void printTemplateArguments(const TemplateArgumentList &Args,
const TemplateParameterList *Params = nullptr);
void prettyPrintAttributes(Decl *D);
Expand All @@ -126,6 +128,18 @@ void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy,
Printer.Visit(const_cast<Decl*>(this));
}

void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context,
bool OmitTemplateKW) const {
print(Out, Context, Context.getPrintingPolicy(), OmitTemplateKW);
}

void TemplateParameterList::print(raw_ostream &Out, const ASTContext &Context,
const PrintingPolicy &Policy,
bool OmitTemplateKW) const {
DeclPrinter Printer(Out, Policy, Context);
Printer.printTemplateParameters(this, OmitTemplateKW);
}

static QualType GetBaseType(QualType T) {
// FIXME: This should be on the Type class!
QualType BaseType = T;
Expand Down Expand Up @@ -1002,25 +1016,35 @@ void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
Visit(*D->decls_begin());
}

void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params) {
void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
bool OmitTemplateKW) {
assert(Params);

Out << "template <";
if (!OmitTemplateKW)
Out << "template ";
Out << '<';

for (unsigned i = 0, e = Params->size(); i != e; ++i) {
if (i != 0)
bool NeedComma = false;
for (const Decl *Param : *Params) {
if (Param->isImplicit())
continue;

if (NeedComma)
Out << ", ";
else
NeedComma = true;

const Decl *Param = Params->getParam(i);
if (auto TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {

if (TTP->wasDeclaredWithTypename())
Out << "typename ";
Out << "typename";
else
Out << "class ";
Out << "class";

if (TTP->isParameterPack())
Out << "...";
Out << " ...";
else if (!TTP->getName().empty())
Out << ' ';

Out << *TTP;

Expand All @@ -1045,7 +1069,9 @@ void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params) {
}
}

Out << "> ";
Out << '>';
if (!OmitTemplateKW)
Out << ' ';
}

void DeclPrinter::printTemplateArguments(const TemplateArgumentList &Args,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ExprCXX.cpp
Expand Up @@ -1204,7 +1204,11 @@ CXXMethodDecl *LambdaExpr::getCallOperator() const {
TemplateParameterList *LambdaExpr::getTemplateParameterList() const {
CXXRecordDecl *Record = getLambdaClass();
return Record->getGenericLambdaTemplateParameterList();
}

ArrayRef<NamedDecl *> LambdaExpr::getExplicitTemplateParameters() const {
const CXXRecordDecl *Record = getLambdaClass();
return Record->getLambdaExplicitTemplateParameters();
}

CompoundStmt *LambdaExpr::getBody() const {
Expand Down
24 changes: 23 additions & 1 deletion clang/lib/AST/ItaniumMangle.cpp
Expand Up @@ -486,6 +486,7 @@ class CXXNameMangler {
const AbiTagList *AdditionalAbiTags);
void mangleBlockForPrefix(const BlockDecl *Block);
void mangleUnqualifiedBlock(const BlockDecl *Block);
void mangleTemplateParamDecl(const NamedDecl *Decl);
void mangleLambda(const CXXRecordDecl *Lambda);
void mangleNestedName(const NamedDecl *ND, const DeclContext *DC,
const AbiTagList *AdditionalAbiTags,
Expand Down Expand Up @@ -1372,7 +1373,8 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
// <unnamed-type-name> ::= <closure-type-name>
//
// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
// <lambda-sig> ::= <parameter-type>+ # Parameter types or 'v' for 'void'.
// <lambda-sig> ::= <template-param-decl>* <parameter-type>+
// # Parameter types or 'v' for 'void'.
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(TD)) {
if (Record->isLambda() && Record->getLambdaManglingNumber()) {
assert(!AdditionalAbiTags &&
Expand Down Expand Up @@ -1678,6 +1680,24 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) {
Out << '_';
}

// <template-param-decl>
// ::= Ty # template type parameter
// ::= Tn <type> # template non-type parameter
// ::= Tt <template-param-decl>* E # template template parameter
void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) {
if (isa<TemplateTypeParmDecl>(Decl)) {
Out << "Ty";
} else if (auto *Tn = dyn_cast<NonTypeTemplateParmDecl>(Decl)) {
Out << "Tn";
mangleType(Tn->getType());
} else if (auto *Tt = dyn_cast<TemplateTemplateParmDecl>(Decl)) {
Out << "Tt";
for (auto *Param : *Tt->getTemplateParameters())
mangleTemplateParamDecl(Param);
Out << "E";
}
}

void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
// If the context of a closure type is an initializer for a class member
// (static or nonstatic), it is encoded in a qualified name with a final
Expand Down Expand Up @@ -1705,6 +1725,8 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
}

Out << "Ul";
for (auto *D : Lambda->getLambdaExplicitTemplateParameters())
mangleTemplateParamDecl(D);
const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()->
getAs<FunctionProtoType>();
mangleBareFunctionType(Proto, /*MangleReturnType=*/false,
Expand Down
11 changes: 8 additions & 3 deletions clang/lib/AST/StmtPrinter.cpp
Expand Up @@ -1900,8 +1900,14 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
}
OS << ']';

if (!Node->getExplicitTemplateParameters().empty()) {
Node->getTemplateParameterList()->print(
OS, Node->getLambdaClass()->getASTContext(),
/*OmitTemplateKW*/true);
}

if (Node->hasExplicitParameters()) {
OS << " (";
OS << '(';
CXXMethodDecl *Method = Node->getCallOperator();
NeedComma = false;
for (const auto *P : Method->parameters()) {
Expand Down Expand Up @@ -1936,9 +1942,8 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
}

// Print the body.
CompoundStmt *Body = Node->getBody();
OS << ' ';
PrintStmt(Body);
PrintRawCompoundStmt(Node->getBody());
}

void StmtPrinter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *Node) {
Expand Down
14 changes: 12 additions & 2 deletions clang/lib/AST/TypePrinter.cpp
Expand Up @@ -1217,8 +1217,18 @@ void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T,
raw_ostream &OS) {
if (IdentifierInfo *Id = T->getIdentifier())
OS << Id->getName();
else
OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex();
else {
bool IsLambdaAutoParam = false;
if (auto D = T->getDecl()) {
if (auto M = dyn_cast_or_null<CXXMethodDecl>(D->getDeclContext()))
IsLambdaAutoParam = D->isImplicit() && M->getParent()->isLambda();
}

if (IsLambdaAutoParam)
OS << "auto";
else
OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex();
}
spaceBeforePlaceHolder(OS);
}

Expand Down

0 comments on commit 8205a81

Please sign in to comment.