Skip to content

Commit f0891cd

Browse files
author
Arthur O'Dwyer
committed
[clang] [concepts] Check constrained-auto return types for void-returning functions
Fixes #49188. Differential Revision: https://reviews.llvm.org/D119184
1 parent adf6703 commit f0891cd

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

clang/lib/Sema/SemaDecl.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -14674,18 +14674,20 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1467414674
if (getLangOpts().CPlusPlus14) {
1467514675
if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() &&
1467614676
FD->getReturnType()->isUndeducedType()) {
14677-
// If the function has a deduced result type but contains no 'return'
14678-
// statements, the result type as written must be exactly 'auto', and
14679-
// the deduced result type is 'void'.
14677+
// For a function with a deduced result type to return void,
14678+
// the result type as written must be 'auto' or 'decltype(auto)',
14679+
// possibly cv-qualified or constrained, but not ref-qualified.
1468014680
if (!FD->getReturnType()->getAs<AutoType>()) {
1468114681
Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto)
1468214682
<< FD->getReturnType();
1468314683
FD->setInvalidDecl();
1468414684
} else {
14685-
// Substitute 'void' for the 'auto' in the type.
14686-
TypeLoc ResultType = getReturnTypeLoc(FD);
14687-
Context.adjustDeducedFunctionResultType(
14688-
FD, SubstAutoType(ResultType.getType(), Context.VoidTy));
14685+
// Falling off the end of the function is the same as 'return;'.
14686+
Expr *Dummy = nullptr;
14687+
if (DeduceFunctionTypeFromReturnExpr(
14688+
FD, dcl->getLocation(), Dummy,
14689+
FD->getReturnType()->getAs<AutoType>()))
14690+
FD->setInvalidDecl();
1468914691
}
1469014692
}
1469114693
} else if (getLangOpts().CPlusPlus11 && isLambdaCallOperator(FD)) {

clang/lib/Sema/SemaStmt.cpp

+15-8
Original file line numberDiff line numberDiff line change
@@ -3808,19 +3808,26 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
38083808
LocalTypedefNameReferencer Referencer(*this);
38093809
Referencer.TraverseType(RetExpr->getType());
38103810
} else {
3811-
// In the case of a return with no operand, the initializer is considered
3812-
// to be void().
3813-
//
3814-
// Deduction here can only succeed if the return type is exactly 'cv auto'
3815-
// or 'decltype(auto)', so just check for that case directly.
3811+
// For a function with a deduced result type to return void,
3812+
// the result type as written must be 'auto' or 'decltype(auto)',
3813+
// possibly cv-qualified or constrained, but not ref-qualified.
38163814
if (!OrigResultType.getType()->getAs<AutoType>()) {
38173815
Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto)
38183816
<< OrigResultType.getType();
38193817
return true;
38203818
}
3821-
// We always deduce U = void in this case.
3822-
Deduced = SubstAutoType(OrigResultType.getType(), Context.VoidTy);
3823-
if (Deduced.isNull())
3819+
// In the case of a return with no operand, the initializer is considered
3820+
// to be 'void()'.
3821+
Expr *Dummy = new (Context) CXXScalarValueInitExpr(
3822+
Context.VoidTy,
3823+
Context.getTrivialTypeSourceInfo(Context.VoidTy, ReturnLoc), ReturnLoc);
3824+
DeduceAutoResult DAR = DeduceAutoType(OrigResultType, Dummy, Deduced);
3825+
3826+
if (DAR == DAR_Failed && !FD->isInvalidDecl())
3827+
Diag(ReturnLoc, diag::err_auto_fn_deduction_failure)
3828+
<< OrigResultType.getType() << Dummy->getType();
3829+
3830+
if (DAR != DAR_Succeeded)
38243831
return true;
38253832
}
38263833

clang/test/SemaTemplate/concepts.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,42 @@ namespace PR50561 {
169169
template<C T, typename U> void f(T, U) = delete;
170170
void g() { f(0, 0); }
171171
}
172+
173+
namespace PR49188 {
174+
template<class T> concept C = false; // expected-note 6 {{because 'false' evaluated to false}}
175+
176+
C auto f1() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
177+
return void();
178+
}
179+
C auto f2() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
180+
return;
181+
}
182+
C auto f3() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
183+
}
184+
C decltype(auto) f4() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
185+
return void();
186+
}
187+
C decltype(auto) f5() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
188+
return;
189+
}
190+
C decltype(auto) f6() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
191+
}
192+
C auto& f7() { // expected-error {{cannot form a reference to 'void'}}
193+
return void();
194+
}
195+
C auto& f8() {
196+
return; // expected-error {{cannot deduce return type 'C auto &' from omitted return expression}}
197+
}
198+
C auto& f9() { // expected-error {{cannot deduce return type 'C auto &' for function with no return statements}}
199+
}
200+
}
201+
namespace PR53911 {
202+
template<class T> concept C = false;
203+
204+
C auto *f1() {
205+
return (void*)nullptr; // FIXME: should error
206+
}
207+
C auto *f2() {
208+
return (int*)nullptr; // FIXME: should error
209+
}
210+
}

0 commit comments

Comments
 (0)