Skip to content

Commit

Permalink
[clang] [concepts] Check constrained-auto return types for void-retur…
Browse files Browse the repository at this point in the history
…ning functions

Fixes #49188.

Differential Revision: https://reviews.llvm.org/D119184
  • Loading branch information
Arthur O'Dwyer committed Mar 4, 2022
1 parent adf6703 commit f0891cd
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 15 deletions.
16 changes: 9 additions & 7 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14674,18 +14674,20 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
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'.
// For a function with a deduced result type to return void,
// the result type as written must be 'auto' or 'decltype(auto)',
// possibly cv-qualified or constrained, but not ref-qualified.
if (!FD->getReturnType()->getAs<AutoType>()) {
Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto)
<< FD->getReturnType();
FD->setInvalidDecl();
} else {
// Substitute 'void' for the 'auto' in the type.
TypeLoc ResultType = getReturnTypeLoc(FD);
Context.adjustDeducedFunctionResultType(
FD, SubstAutoType(ResultType.getType(), Context.VoidTy));
// Falling off the end of the function is the same as 'return;'.
Expr *Dummy = nullptr;
if (DeduceFunctionTypeFromReturnExpr(
FD, dcl->getLocation(), Dummy,
FD->getReturnType()->getAs<AutoType>()))
FD->setInvalidDecl();
}
}
} else if (getLangOpts().CPlusPlus11 && isLambdaCallOperator(FD)) {
Expand Down
23 changes: 15 additions & 8 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3808,19 +3808,26 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
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.
// For a function with a deduced result type to return void,
// the result type as written must be 'auto' or 'decltype(auto)',
// possibly cv-qualified or constrained, but not ref-qualified.
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())
// In the case of a return with no operand, the initializer is considered
// to be 'void()'.
Expr *Dummy = new (Context) CXXScalarValueInitExpr(
Context.VoidTy,
Context.getTrivialTypeSourceInfo(Context.VoidTy, ReturnLoc), ReturnLoc);
DeduceAutoResult DAR = DeduceAutoType(OrigResultType, Dummy, Deduced);

if (DAR == DAR_Failed && !FD->isInvalidDecl())
Diag(ReturnLoc, diag::err_auto_fn_deduction_failure)
<< OrigResultType.getType() << Dummy->getType();

if (DAR != DAR_Succeeded)
return true;
}

Expand Down
39 changes: 39 additions & 0 deletions clang/test/SemaTemplate/concepts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,42 @@ namespace PR50561 {
template<C T, typename U> void f(T, U) = delete;
void g() { f(0, 0); }
}

namespace PR49188 {
template<class T> concept C = false; // expected-note 6 {{because 'false' evaluated to false}}

C auto f1() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
return void();
}
C auto f2() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
return;
}
C auto f3() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
}
C decltype(auto) f4() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
return void();
}
C decltype(auto) f5() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
return;
}
C decltype(auto) f6() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
}
C auto& f7() { // expected-error {{cannot form a reference to 'void'}}
return void();
}
C auto& f8() {
return; // expected-error {{cannot deduce return type 'C auto &' from omitted return expression}}
}
C auto& f9() { // expected-error {{cannot deduce return type 'C auto &' for function with no return statements}}
}
}
namespace PR53911 {
template<class T> concept C = false;

C auto *f1() {
return (void*)nullptr; // FIXME: should error
}
C auto *f2() {
return (int*)nullptr; // FIXME: should error
}
}

0 comments on commit f0891cd

Please sign in to comment.