Implement a rudimentary form of generic lambdas.
Specifically, the following features are not included in this commit:
- any sort of capturing within generic lambdas
- nested lambdas
- conversion operator for captureless lambdas
- ensuring all visitors are generic lambda aware
As an example of what compiles:
template <class F1, class F2>
struct overload : F1, F2 {
using F1::operator();
using F2::operator();
overload(F1 f1, F2 f2) : F1(f1), F2(f2) { }
};
auto Recursive = [](auto Self, auto h, auto ... rest) {
return 1 + Self(Self, rest...);
};
auto Base = [](auto Self, auto h) {
return 1;
};
overload<decltype(Base), decltype(Recursive)> O(Base, Recursive);
int num_params = O(O, 5, 3, "abc", 3.14, 'a');
Please see attached tests for more examples.
Some implementation notes:
- Add a new Declarator context => LambdaExprParameterContext to
clang::Declarator to allow the use of 'auto' in declaring generic
lambda parameters
- Augment AutoType's constructor (similar to how variadic
template-type-parameters ala TemplateTypeParmDecl are implemented) to
accept an IsParameterPack to encode a generic lambda parameter pack.
- Add various helpers to CXXRecordDecl to facilitate identifying
and querying a closure class
- LambdaScopeInfo (which maintains the current lambda's Sema state)
was augmented to house the current depth of the template being
parsed (id est the Parser calls Sema::RecordParsingTemplateParameterDepth)
so that Sema::ActOnLambdaAutoParameter may use it to create the
appropriate list of corresponding TemplateTypeParmDecl for each
auto parameter identified within the generic lambda (also stored
within the current LambdaScopeInfo). Additionally,
a TemplateParameterList data-member was added to hold the invented
TemplateParameterList AST node which will be much more useful
once we teach TreeTransform how to transform generic lambdas.
- SemaLambda.h was added to hold some common lambda utility
functions (this file is likely to grow ...)
- Teach Sema::ActOnStartOfFunctionDef to check whether it
is being called to instantiate a generic lambda's call
operator, and if so, push an appropriately prepared
LambdaScopeInfo object on the stack.
- Teach Sema::ActOnStartOfLambdaDefinition to set the
return type of a lambda without a trailing return type
to 'auto' in C++1y mode, and teach the return type
deduction machinery in SemaStmt.cpp to process either
C++11 and C++14 lambda's correctly depending on the flag.
- various tests were added - but much more will be needed.
A greatful thanks to all reviewers including Eli Friedman,
James Dennett and the ever illuminating Richard Smith. And
yet I am certain that I have allowed unidentified bugs to creep in;
bugs, that I will do my best to slay, once identified!
Thanks!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188977 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 0570a60..5434e98 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -2487,12 +2487,31 @@
// [expr.prim.lambda]p4 in C++11; block literals follow the same rules.
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
QualType FnRetType = CurCap->ReturnType;
-
- // For blocks/lambdas with implicit return types, we check each return
- // statement individually, and deduce the common return type when the block
- // or lambda is completed.
- if (CurCap->HasImplicitReturnType) {
- // FIXME: Fold this into the 'auto' codepath below.
+ LambdaScopeInfo *const LambdaSI = getCurLambda();
+ // In C++1y, an implicit return type behaves as if 'auto' was
+ // the return type.
+ if (FnRetType.isNull() && getLangOpts().CPlusPlus1y) {
+ if (LambdaSI) {
+ FunctionDecl *CallOp = LambdaSI->CallOperator;
+ FnRetType = CallOp->getResultType();
+ assert(FnRetType->getContainedAutoType());
+ }
+ }
+
+ // For blocks/lambdas with implicit return types in C++11, we check each
+ // return statement individually, and deduce the common return type when
+ // the block or lambda is completed. In C++1y, the return type deduction
+ // of a lambda is specified in terms of auto.
+ // Notably, in C++11, we take the type of the expression after decay and
+ // lvalue-to-rvalue conversion, so a class type can be cv-qualified.
+ // In C++1y, we perform template argument deduction as if the return
+ // type were 'auto', so an implicit return type is never cv-qualified.
+ // i.e if (getLangOpts().CPlusPlus1y && FnRetType.hasQualifiers())
+ // FnRetType = FnRetType.getUnqualifiedType();
+ // Return type deduction is unchanged for blocks in C++1y.
+ // FIXME: Fold this into the 'auto' codepath below.
+ if (CurCap->HasImplicitReturnType &&
+ (!LambdaSI || !getLangOpts().CPlusPlus1y)) {
if (RetValExp && !isa<InitListExpr>(RetValExp)) {
ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp);
if (Result.isInvalid())
@@ -2500,13 +2519,7 @@
RetValExp = Result.take();
if (!CurContext->isDependentContext()) {
- FnRetType = RetValExp->getType();
- // In C++11, we take the type of the expression after decay and
- // lvalue-to-rvalue conversion, so a class type can be cv-qualified.
- // In C++1y, we perform template argument deduction as if the return
- // type were 'auto', so an implicit return type is never cv-qualified.
- if (getLangOpts().CPlusPlus1y && FnRetType.hasQualifiers())
- FnRetType = FnRetType.getUnqualifiedType();
+ FnRetType = RetValExp->getType();
} else
FnRetType = CurCap->ReturnType = Context.DependentTy;
} else {
@@ -2517,7 +2530,6 @@
Diag(ReturnLoc, diag::err_lambda_return_init_list)
<< RetValExp->getSourceRange();
}
-
FnRetType = Context.VoidTy;
}
@@ -2526,7 +2538,8 @@
if (CurCap->ReturnType.isNull())
CurCap->ReturnType = FnRetType;
} else if (AutoType *AT =
- FnRetType.isNull() ? 0 : FnRetType->getContainedAutoType()) {
+ (FnRetType.isNull() || !LambdaSI) ? 0
+ : FnRetType->getContainedAutoType()) {
// In C++1y, the return type may involve 'auto'.
FunctionDecl *FD = cast<LambdaScopeInfo>(CurCap)->CallOperator;
if (CurContext->isDependentContext()) {
@@ -2534,7 +2547,7 @@
// Return type deduction [...] occurs when the definition is
// instantiated even if the function body contains a return
// statement with a non-type-dependent operand.
- CurCap->ReturnType = FnRetType = Context.DependentTy;
+ CurCap->ReturnType = FnRetType;
} else if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) {
FD->setInvalidDecl();
return StmtError();
@@ -2564,7 +2577,7 @@
// pickier with blocks than for normal functions because we don't have GCC
// compatibility to worry about here.
const VarDecl *NRVOCandidate = 0;
- if (FnRetType->isDependentType()) {
+ if (FnRetType->isDependentType() || FnRetType->isUndeducedType()) {
// Delay processing for now. TODO: there are lots of dependent
// types we can conclusively prove aren't void.
} else if (FnRetType->isVoidType()) {
@@ -2624,7 +2637,6 @@
return Owned(Result);
}
-
/// Deduce the return type for a function from a returned expression, per
/// C++1y [dcl.spec.auto]p6.
bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
@@ -2634,7 +2646,6 @@
TypeLoc OrigResultType = FD->getTypeSourceInfo()->getTypeLoc().
IgnoreParens().castAs<FunctionProtoTypeLoc>().getResultLoc();
QualType Deduced;
-
if (RetExpr && isa<InitListExpr>(RetExpr)) {
// If the deduction is for a return statement and the initializer is
// a braced-init-list, the program is ill-formed.
@@ -2692,9 +2703,18 @@
AutoType *NewAT = Deduced->getContainedAutoType();
if (!FD->isDependentContext() &&
!Context.hasSameType(AT->getDeducedType(), NewAT->getDeducedType())) {
- Diag(ReturnLoc, diag::err_auto_fn_different_deductions)
- << (AT->isDecltypeAuto() ? 1 : 0)
- << NewAT->getDeducedType() << AT->getDeducedType();
+ LambdaScopeInfo *const LambdaSI = getCurLambda();
+ if (LambdaSI && LambdaSI->HasImplicitReturnType) {
+ Diag(ReturnLoc,
+ diag::err_typecheck_missing_return_type_incompatible)
+ << NewAT->getDeducedType() << AT->getDeducedType()
+ << true /*IsLambda*/;
+ }
+ else {
+ Diag(ReturnLoc, diag::err_auto_fn_different_deductions)
+ << (AT->isDecltypeAuto() ? 1 : 0)
+ << NewAT->getDeducedType() << AT->getDeducedType();
+ }
return true;
}
} else if (!FD->isInvalidDecl()) {
@@ -2710,10 +2730,8 @@
// Check for unexpanded parameter packs.
if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
return StmtError();
-
if (isa<CapturingScopeInfo>(getCurFunction()))
return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
-
QualType FnRetType;
QualType RelatedRetType;
if (const FunctionDecl *FD = getCurFunctionDecl()) {