diff options
Diffstat (limited to 'lib/Sema/SemaStmt.cpp')
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 91 |
1 files changed, 88 insertions, 3 deletions
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 16cc43a27f..248665ac86 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -2482,12 +2482,80 @@ 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, + SourceLocation ReturnLoc, + Expr *&RetExpr, + AutoType *AT) { + TypeLoc OrigResultType = FD->getTypeSourceInfo()->getTypeLoc(). + IgnoreParens().castAs<FunctionProtoTypeLoc>().getResultLoc(); + QualType Deduced; + + if (RetExpr) { + // If the deduction is for a return statement and the initializer is + // a braced-init-list, the program is ill-formed. + if (isa<InitListExpr>(RetExpr)) { + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_return_init_list); + return true; + } + + // Otherwise, [...] deduce a value for U using the rules of template + // argument deduction. + DeduceAutoResult DAR = DeduceAutoType(OrigResultType, RetExpr, Deduced); + + if (DAR == DAR_Failed && !FD->isInvalidDecl()) + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_deduction_failure) + << OrigResultType.getType() << RetExpr->getType(); + + if (DAR != DAR_Succeeded) + return true; + } else { + // In the case of a return with no operand, the initializer is considered + // to be void(). + // + // Deduction here can only succeed if the return type is exactly 'cv auto' + // or 'decltype(auto)', so just check for that case directly. + if (!OrigResultType.getType()->getAs<AutoType>()) { + Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto) + << OrigResultType.getType(); + return true; + } + // We always deduce U = void in this case. + Deduced = SubstAutoType(OrigResultType.getType(), Context.VoidTy); + if (Deduced.isNull()) + return true; + } + + // If a function with a declared return type that contains a placeholder type + // has multiple return statements, the return type is deduced for each return + // statement. [...] if the type deduced is not the same in each deduction, + // the program is ill-formed. + if (AT->isDeduced() && !FD->isInvalidDecl()) { + AutoType *NewAT = Deduced->getContainedAutoType(); + if (!Context.hasSameType(AT->getDeducedType(), NewAT->getDeducedType())) { + Diag(ReturnLoc, diag::err_auto_fn_different_deductions) + << (AT->isDecltypeAuto() ? 1 : 0) + << NewAT->getDeducedType() << AT->getDeducedType(); + return true; + } + } else if (!FD->isInvalidDecl()) { + // Update all declarations of the function to have the deduced return type. + Context.adjustDeducedFunctionResultType(FD, Deduced); + } + + return false; +} + StmtResult Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // Check for unexpanded parameter packs. if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); + // FIXME: Unify this and C++1y auto function handling. In particular, we + // should allow 'return { 1, 2, 3 };' in a lambda to deduce + // 'std::initializer_list<int>'. if (isa<CapturingScopeInfo>(getCurFunction())) return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); @@ -2510,6 +2578,23 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } else // If we don't have a function/method context, bail. return StmtError(); + // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing + // deduction. + bool HasDependentReturnType = FnRetType->isDependentType(); + if (getLangOpts().CPlusPlus1y) { + if (AutoType *AT = FnRetType->getContainedAutoType()) { + FunctionDecl *FD = cast<FunctionDecl>(CurContext); + if (CurContext->isDependentContext()) + HasDependentReturnType = true; + else if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { + FD->setInvalidDecl(); + return StmtError(); + } else { + FnRetType = FD->getResultType(); + } + } + } + ReturnStmt *Result = 0; if (FnRetType->isVoidType()) { if (RetValExp) { @@ -2575,7 +2660,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, 0); - } else if (!RetValExp && !FnRetType->isDependentType()) { + } else if (!RetValExp && !HasDependentReturnType) { unsigned DiagID = diag::warn_return_missing_expr; // C90 6.6.6.4p4 // C99 6.8.6.4p1 (ext_ since GCC warns) if (getLangOpts().C99) DiagID = diag::ext_return_missing_expr; @@ -2586,9 +2671,9 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { Diag(ReturnLoc, DiagID) << getCurMethodDecl()->getDeclName() << 1/*meth*/; Result = new (Context) ReturnStmt(ReturnLoc); } else { - assert(RetValExp || FnRetType->isDependentType()); + assert(RetValExp || HasDependentReturnType); const VarDecl *NRVOCandidate = 0; - if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) { + if (!HasDependentReturnType && !RetValExp->isTypeDependent()) { // we have a non-void function with an expression, continue checking QualType RetType = (RelatedRetType.isNull() ? FnRetType : RelatedRetType); |