From: Richard Smith Date: Wed, 25 Sep 2013 05:02:54 +0000 (+0000) Subject: Refactor to use C++1y 'auto' semantics directly in lambdas with no specified X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=41d0958281627882fbe2049fb887d741eabd3fe3;p=clang Refactor to use C++1y 'auto' semantics directly in lambdas with no specified return type in C++1y mode. No functionality change intended. Extracted and tweaked from a patch by Faisal Vali! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191354 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 761feffc26..ec69ef8ed0 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -131,10 +131,25 @@ Sema::ExpressionEvaluationContextRecord::getMangleNumberingContext( } CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class, - SourceRange IntroducerRange, - TypeSourceInfo *MethodType, - SourceLocation EndLoc, - ArrayRef Params) { + SourceRange IntroducerRange, + TypeSourceInfo *MethodTypeInfo, + SourceLocation EndLoc, + ArrayRef Params) { + QualType MethodType = MethodTypeInfo->getType(); + + // If a lambda appears in a dependent context and has an 'auto' return type, + // deduce it to a dependent type. + // FIXME: Generic lambda call operators should also get this treatment. + if (Class->isDependentContext()) { + const FunctionProtoType *FPT = MethodType->castAs(); + QualType Result = FPT->getResultType(); + if (Result->isUndeducedType()) { + Result = SubstAutoType(Result, Context.DependentTy); + MethodType = Context.getFunctionType(Result, FPT->getArgTypes(), + FPT->getExtProtoInfo()); + } + } + // C++11 [expr.prim.lambda]p5: // The closure type for a lambda-expression has a public inline function // call operator (13.5.4) whose parameters and return type are described by @@ -152,7 +167,7 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class, DeclarationNameInfo(MethodName, IntroducerRange.getBegin(), MethodNameLoc), - MethodType->getType(), MethodType, + MethodType, MethodTypeInfo, SC_None, /*isInline=*/true, /*isConstExpr=*/false, @@ -553,8 +568,17 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, /*IsVariadic=*/false, /*IsCXXMethod=*/true)); EPI.HasTrailingReturn = true; EPI.TypeQuals |= DeclSpec::TQ_const; - QualType MethodTy = Context.getFunctionType(Context.DependentTy, None, - EPI); + // C++1y [expr.prim.lambda]: + // The lambda return type is 'auto', which is replaced by the + // trailing-return type if provided and/or deduced from 'return' + // statements + // We don't do this before C++1y, because we don't support deduced return + // types there. + QualType DefaultTypeForNoTrailingReturn = + getLangOpts().CPlusPlus1y ? Context.getAutoDeductType() + : Context.DependentTy; + QualType MethodTy = + Context.getFunctionType(DefaultTypeForNoTrailingReturn, None, EPI); MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy); ExplicitParams = false; ExplicitResultType = false; @@ -563,21 +587,19 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, assert(ParamInfo.isFunctionDeclarator() && "lambda-declarator is a function"); DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo(); - + // C++11 [expr.prim.lambda]p5: // This function call operator is declared const (9.3.1) if and only if // the lambda-expression's parameter-declaration-clause is not followed // by mutable. It is neither virtual nor declared volatile. [...] if (!FTI.hasMutableQualifier()) FTI.TypeQuals |= DeclSpec::TQ_const; - + MethodTyInfo = GetTypeForDeclarator(ParamInfo, CurScope); assert(MethodTyInfo && "no type from lambda-declarator"); EndLoc = ParamInfo.getSourceRange().getEnd(); - - ExplicitResultType - = MethodTyInfo->getType()->getAs()->getResultType() - != Context.DependentTy; + + ExplicitResultType = FTI.hasTrailingReturnType(); if (FTI.NumArgs == 1 && !FTI.isVariadic && FTI.ArgInfo[0].Ident == 0 && cast(FTI.ArgInfo[0].Param)->getType()->isVoidType()) { @@ -1015,8 +1037,12 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, // If a lambda-expression does not include a // trailing-return-type, it is as if the trailing-return-type // denotes the following type: + // + // Skip for C++1y return type deduction semantics which uses + // different machinery. + // FIXME: Refactor and Merge the return type deduction machinery. // FIXME: Assumes current resolution to core issue 975. - if (LSI->HasImplicitReturnType) { + if (LSI->HasImplicitReturnType && !getLangOpts().CPlusPlus1y) { deduceClosureReturnType(*LSI); // - if there are no return statements in the diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index ef2202f337..136a5e169d 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -2478,6 +2478,14 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, return Res; } +/// \brief Determine whether the declared return type of the specified function +/// contains 'auto'. +static bool hasDeducedReturnType(FunctionDecl *FD) { + const FunctionProtoType *FPT = + FD->getTypeSourceInfo()->getType()->castAs(); + return FPT->getResultType()->isUndeducedType(); +} + /// ActOnCapScopeReturnStmt - Utility routine to type-check return statements /// for capturing scopes. /// @@ -2487,27 +2495,36 @@ 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; + LambdaScopeInfo *CurLambda = dyn_cast(CurCap); + + if (CurLambda && hasDeducedReturnType(CurLambda->CallOperator)) { + // In C++1y, the return type may involve 'auto'. + // FIXME: Blocks might have a return type of 'auto' explicitly specified. + FunctionDecl *FD = CurLambda->CallOperator; + if (CurCap->ReturnType.isNull()) + CurCap->ReturnType = FD->getResultType(); - // 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. + AutoType *AT = CurCap->ReturnType->getContainedAutoType(); + assert(AT && "lost auto type from lambda return type"); + if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { + FD->setInvalidDecl(); + return StmtError(); + } + CurCap->ReturnType = FnRetType = FD->getResultType(); + } else if (CurCap->HasImplicitReturnType) { + // 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. + // FIXME: Fold this into the 'auto' codepath above. if (RetValExp && !isa(RetValExp)) { ExprResult Result = DefaultFunctionArrayLvalueConversion(RetValExp); if (Result.isInvalid()) return StmtError(); RetValExp = Result.take(); - if (!CurContext->isDependentContext()) { + 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(); - } else + else FnRetType = CurCap->ReturnType = Context.DependentTy; } else { if (RetValExp) { @@ -2525,21 +2542,6 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // make sure we provide a return type now for better error recovery. if (CurCap->ReturnType.isNull()) CurCap->ReturnType = FnRetType; - } else if (AutoType *AT = - FnRetType.isNull() ? 0 : FnRetType->getContainedAutoType()) { - // In C++1y, the return type may involve 'auto'. - FunctionDecl *FD = cast(CurCap)->CallOperator; - if (CurContext->isDependentContext()) { - // C++1y [dcl.spec.auto]p12: - // 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; - } else if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { - FD->setInvalidDecl(); - return StmtError(); - } else - CurCap->ReturnType = FnRetType = FD->getResultType(); } assert(!FnRetType.isNull()); @@ -2553,8 +2555,9 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { Diag(ReturnLoc, diag::err_return_in_captured_stmt) << CurRegion->getRegionName(); return StmtError(); } else { - LambdaScopeInfo *LSI = cast(CurCap); - if (LSI->CallOperator->getType()->getAs()->getNoReturnAttr()){ + assert(CurLambda && "unknown kind of captured scope"); + if (CurLambda->CallOperator->getType()->getAs() + ->getNoReturnAttr()) { Diag(ReturnLoc, diag::err_noreturn_lambda_has_return_expr); return StmtError(); } @@ -2638,7 +2641,10 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, 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. - Diag(RetExpr->getExprLoc(), diag::err_auto_fn_return_init_list); + Diag(RetExpr->getExprLoc(), + getCurLambda() ? diag::err_lambda_return_init_list + : diag::err_auto_fn_return_init_list) + << RetExpr->getSourceRange(); return true; } @@ -2692,9 +2698,16 @@ 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(); + const LambdaScopeInfo *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()) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 753ce876d6..1f40eb8d1d 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -762,8 +762,13 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // is inferred from the return statements inside the block. // The declspec is always missing in a lambda expr context; it is either // specified with a trailing return type or inferred. - if (declarator.getContext() == Declarator::LambdaExprContext || - isOmittedBlockReturnType(declarator)) { + if (S.getLangOpts().CPlusPlus1y && + declarator.getContext() == Declarator::LambdaExprContext) { + // In C++1y, a lambda's implicit return type is 'auto'. + Result = Context.getAutoDeductType(); + break; + } else if (declarator.getContext() == Declarator::LambdaExprContext || + isOmittedBlockReturnType(declarator)) { Result = Context.DependentTy; break; }