From fd5277c0635f74bbdbb2daa3bb95e08f39db7d63 Mon Sep 17 00:00:00 2001 From: Faisal Vali Date: Thu, 22 Aug 2013 01:49:11 +0000 Subject: 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 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 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! llvm-svn: 188977 --- clang/lib/Sema/SemaStmt.cpp | 66 ++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 24 deletions(-) (limited to 'clang/lib/Sema/SemaStmt.cpp') diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 0570a60a0a0..5434e987e9e 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2487,12 +2487,31 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // [expr.prim.lambda]p4 in C++11; block literals follow the same rules. CapturingScopeInfo *CurCap = cast(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(RetValExp)) { ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp); if (Result.isInvalid()) @@ -2500,13 +2519,7 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { 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 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { Diag(ReturnLoc, diag::err_lambda_return_init_list) << RetValExp->getSourceRange(); } - FnRetType = Context.VoidTy; } @@ -2526,7 +2538,8 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { 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(CurCap)->CallOperator; if (CurContext->isDependentContext()) { @@ -2534,7 +2547,7 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // 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 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // 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 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { 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 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, TypeLoc OrigResultType = FD->getTypeSourceInfo()->getTypeLoc(). IgnoreParens().castAs().getResultLoc(); QualType Deduced; - if (RetExpr && isa(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 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, 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 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // Check for unexpanded parameter packs. if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); - if (isa(getCurFunction())) return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); - QualType FnRetType; QualType RelatedRetType; if (const FunctionDecl *FD = getCurFunctionDecl()) { -- cgit v1.2.3