diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8671,7 +8671,8 @@ bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, SourceLocation ReturnLoc, - Expr *&RetExpr, AutoType *AT); + Expr *&RetExpr, const AutoType *AT, + bool HasReturnStmt); FunctionTemplateDecl *getMoreSpecializedTemplate( FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14660,18 +14660,12 @@ if (getLangOpts().CPlusPlus14) { if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() && FD->getReturnType()->isUndeducedType()) { - // If the function has a deduced result type but contains no 'return' - // statements, the result type as written must be exactly 'auto', and - // the deduced result type is 'void'. - if (!FD->getReturnType()->getAs()) { - Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto) - << FD->getReturnType(); + // Falling off the end of the function is the same as 'return;'. + Expr *Dummy = nullptr; + if (DeduceFunctionTypeFromReturnExpr( + FD, dcl->getLocation(), Dummy, + FD->getReturnType()->getAs(), false)) { FD->setInvalidDecl(); - } else { - // Substitute 'void' for the 'auto' in the type. - TypeLoc ResultType = getReturnTypeLoc(FD); - Context.adjustDeducedFunctionResultType( - FD, SubstAutoType(ResultType.getType(), Context.VoidTy)); } } } else if (getLangOpts().CPlusPlus11 && isLambdaCallOperator(FD)) { diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3590,7 +3590,7 @@ AutoType *AT = CurCap->ReturnType->getContainedAutoType(); assert(AT && "lost auto type from lambda return type"); - if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { + if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT, true)) { FD->setInvalidDecl(); // FIXME: preserve the ill-formed return expression. return StmtError(); @@ -3761,9 +3761,9 @@ /// C++1y [dcl.spec.auto]p6. bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, SourceLocation ReturnLoc, - Expr *&RetExpr, - AutoType *AT) { - // If this is the conversion function for a lambda, we choose to deduce it + Expr *&RetExpr, const AutoType *AT, + bool HasReturnStmt) { + // If this is the conversion function for a lambda, we choose to deduce its // type from the corresponding call operator, not from the synthesized return // statement within it. See Sema::DeduceReturnType. if (isLambdaConversionOperator(FD)) @@ -3808,19 +3808,18 @@ LocalTypedefNameReferencer Referencer(*this); Referencer.TraverseType(RetExpr->getType()); } 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()) { - 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()) + // In the case of a return with no operand, the initializer is considered + // to be 'void()'. + ExprResult R = new (Context) CXXScalarValueInitExpr( + Context.VoidTy, + Context.getTrivialTypeSourceInfo(Context.VoidTy, ReturnLoc), ReturnLoc); + Expr *Dummy = R.get(); + DeduceAutoResult DAR = DeduceAutoType(OrigResultType, Dummy, Deduced); + if (DAR == DAR_Failed && !FD->isInvalidDecl()) + Diag(ReturnLoc, HasReturnStmt ? diag::err_auto_fn_return_void_but_not_auto + : diag::err_auto_fn_no_return_but_not_auto) + << OrigResultType.getType(); + if (DAR != DAR_Succeeded) return true; } @@ -3988,8 +3987,8 @@ // we saw a `return` whose expression had an error, don't keep // trying to deduce its return type. // (Some return values may be needlessly wrapped in RecoveryExpr). - if (FD->isInvalidDecl() || - DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { + if (FD->isInvalidDecl() || DeduceFunctionTypeFromReturnExpr( + FD, ReturnLoc, RetValExp, AT, true)) { FD->setInvalidDecl(); if (!AllowRecovery) return StmtError(); diff --git a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp --- a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp +++ b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp @@ -113,7 +113,7 @@ using Void = void; using Void = decltype(void_ret()); -auto &void_ret_2() {} // expected-error {{cannot deduce return type 'auto &' for function with no return statements}} +auto &void_ret_2() {} // expected-error {{cannot form a reference to 'void'}} const auto void_ret_3() {} // ok, return type 'const void' is adjusted to 'void' const auto void_ret_4() { diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -169,3 +169,23 @@ template void f(T, U) = delete; void g() { f(0, 0); } } + +namespace PR49188 { + template concept C = false; // expected-note 6 {{because 'false' evaluated to false}} + + C auto f1() { return void(); } // expected-error {{deduced type 'void' does not satisfy 'C'}} + C auto f2() { return; } // expected-error {{deduced type 'void' does not satisfy 'C'}} + C auto f3() {} // expected-error {{deduced type 'void' does not satisfy 'C'}} + C decltype(auto) f4() { return void(); } // expected-error {{deduced type 'void' does not satisfy 'C'}} + C decltype(auto) f5() { return; } // expected-error {{deduced type 'void' does not satisfy 'C'}} + C decltype(auto) f6() {} // expected-error {{deduced type 'void' does not satisfy 'C'}} + + void g() { + f1(); + f2(); + f3(); + f4(); + f5(); + f6(); + } +}