Skip to content

Commit 155a155

Browse files
Merge pull request #23251 from nate-chandler/nate/omit-return
Allow return to be omitted from single expression functions.
2 parents bad5b58 + f8ef213 commit 155a155

34 files changed

+2163
-17
lines changed

include/swift/AST/Decl.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,7 @@ class alignas(1 << DeclAlignInBits) Decl {
406406
SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2,
407407
StaticSpelling : 2
408408
);
409-
410-
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1+1,
409+
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1+1+1,
411410
/// \see AbstractFunctionDecl::BodyKind
412411
BodyKind : 3,
413412

@@ -431,7 +430,10 @@ class alignas(1 << DeclAlignInBits) Decl {
431430

432431
/// Whether this member was synthesized as part of a derived
433432
/// protocol conformance.
434-
Synthesized : 1
433+
Synthesized : 1,
434+
435+
/// Whether this member's body consists of a single expression.
436+
HasSingleExpressionBody : 1
435437
);
436438

437439
SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+2+1+1+2,
@@ -5529,13 +5531,25 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
55295531
Bits.AbstractFunctionDecl.NeedsNewVTableEntry = false;
55305532
Bits.AbstractFunctionDecl.HasComputedNeedsNewVTableEntry = false;
55315533
Bits.AbstractFunctionDecl.Synthesized = false;
5534+
Bits.AbstractFunctionDecl.HasSingleExpressionBody = false;
55325535
}
55335536

55345537
void setBodyKind(BodyKind K) {
55355538
Bits.AbstractFunctionDecl.BodyKind = unsigned(K);
55365539
}
55375540

55385541
public:
5542+
void setHasSingleExpressionBody(bool Has = true) {
5543+
Bits.AbstractFunctionDecl.HasSingleExpressionBody = Has;
5544+
}
5545+
5546+
bool hasSingleExpressionBody() const {
5547+
return Bits.AbstractFunctionDecl.HasSingleExpressionBody;
5548+
}
5549+
5550+
Expr *getSingleExpressionBody() const;
5551+
void setSingleExpressionBody(Expr *NewBody);
5552+
55395553
/// Returns the string for the base name, or "_" if this is unnamed.
55405554
StringRef getNameStr() const {
55415555
assert(!getFullName().isSpecial() && "Cannot get string for special names");

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ NOTE(designated_init_c_struct_fix,none,
248248
ERROR(missing_return,none,
249249
"missing return in a %select{function|closure}1 expected to return %0",
250250
(Type, unsigned))
251+
ERROR(missing_return_last_expr,none,
252+
"missing return in a %select{function|closure}1 expected to return %0; "
253+
"did you mean to return the last expression?",
254+
(Type, unsigned))
251255
ERROR(missing_never_call,none,
252256
"%select{function|closure}1 with uninhabited return type %0 is missing "
253257
"call to another never-returning function on all paths",

lib/AST/ASTDumper.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,10 @@ namespace {
10701070
Indent -= 2;
10711071
}
10721072
}
1073-
if (auto Body = D->getBody(/*canSynthesize=*/false)) {
1073+
if (D->hasSingleExpressionBody()) {
1074+
OS << '\n';
1075+
printRec(D->getSingleExpressionBody());
1076+
} else if (auto Body = D->getBody(/*canSynthesize=*/false)) {
10741077
OS << '\n';
10751078
printRec(Body, D->getASTContext());
10761079
}

lib/AST/Decl.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,46 @@ case DeclKind::ID: return cast<ID##Decl>(this)->getLoc();
473473
llvm_unreachable("Unknown decl kind");
474474
}
475475

476+
Expr *AbstractFunctionDecl::getSingleExpressionBody() const {
477+
assert(hasSingleExpressionBody() && "Not a single-expression body");
478+
auto braceStmt = getBody();
479+
assert(braceStmt != nullptr && "No body currently available.");
480+
auto body = getBody()->getElement(0);
481+
if (auto *stmt = body.dyn_cast<Stmt *>()) {
482+
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
483+
return returnStmt->getResult();
484+
} else if (auto *failStmt = dyn_cast<FailStmt>(stmt)) {
485+
// We can only get to this point if we're a type-checked ConstructorDecl
486+
// which was originally spelled init?(...) { nil }.
487+
//
488+
// There no longer is a single-expression to return, so ignore null.
489+
return nullptr;
490+
}
491+
}
492+
return body.get<Expr *>();
493+
}
494+
495+
void AbstractFunctionDecl::setSingleExpressionBody(Expr *NewBody) {
496+
assert(hasSingleExpressionBody() && "Not a single-expression body");
497+
auto body = getBody()->getElement(0);
498+
if (auto *stmt = body.dyn_cast<Stmt *>()) {
499+
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
500+
returnStmt->setResult(NewBody);
501+
return;
502+
} else if (auto *failStmt = dyn_cast<FailStmt>(stmt)) {
503+
// We can only get to this point if we're a type-checked ConstructorDecl
504+
// which was originally spelled init?(...) { nil }.
505+
//
506+
// We can no longer write the single-expression which is being set on us
507+
// into anything because a FailStmt does not have such a child. As a
508+
// result we need to demand that the NewBody is null.
509+
assert(NewBody == nullptr);
510+
return;
511+
}
512+
}
513+
getBody()->setElement(0, NewBody);
514+
}
515+
476516
bool AbstractStorageDecl::isTransparent() const {
477517
return getAttrs().hasAttribute<TransparentAttr>();
478518
}

lib/Parse/ParseDecl.cpp

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5671,8 +5671,60 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) {
56715671
Context.Stats->getFrontendCounters().NumFunctionsParsed++;
56725672

56735673
ParserResult<BraceStmt> Body = parseBraceItemList(diag::invalid_diagnostic);
5674-
if (!Body.isNull())
5675-
AFD->setBody(Body.get());
5674+
if (!Body.isNull()) {
5675+
BraceStmt * BS = Body.get();
5676+
AFD->setBody(BS);
5677+
5678+
// If the body consists of a single expression, turn it into a return
5679+
// statement.
5680+
//
5681+
// But don't do this transformation during code completion, as the source
5682+
// may be incomplete and the type mismatch in return statement will just
5683+
// confuse the type checker.
5684+
if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) {
5685+
auto Element = BS->getElement(0);
5686+
if (auto *stmt = Element.dyn_cast<Stmt *>()) {
5687+
auto kind = AFD->getKind();
5688+
if (kind == DeclKind::Var || kind == DeclKind::Subscript ||
5689+
kind == DeclKind::Func ) {
5690+
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
5691+
if (!returnStmt->hasResult()) {
5692+
auto returnExpr = TupleExpr::createEmpty(Context,
5693+
SourceLoc(),
5694+
SourceLoc(),
5695+
/*implicit*/true);
5696+
returnStmt->setResult(returnExpr);
5697+
AFD->setHasSingleExpressionBody();
5698+
AFD->setSingleExpressionBody(returnExpr);
5699+
}
5700+
}
5701+
}
5702+
} else if (auto *E = Element.dyn_cast<Expr *>()) {
5703+
if (auto SE = dyn_cast<SequenceExpr>(E->getSemanticsProvidingExpr())) {
5704+
if (SE->getNumElements() > 1 && isa<AssignExpr>(SE->getElement(1))) {
5705+
// This is an assignment. We don't want to implicitly return
5706+
// it.
5707+
return;
5708+
}
5709+
}
5710+
if (auto F = dyn_cast<FuncDecl>(AFD)) {
5711+
auto RS = new (Context) ReturnStmt(SourceLoc(), E);
5712+
BS->setElement(0, RS);
5713+
AFD->setHasSingleExpressionBody();
5714+
AFD->setSingleExpressionBody(E);
5715+
} else if (auto *F = dyn_cast<ConstructorDecl>(AFD)) {
5716+
if (F->getFailability() != OTK_None && isa<NilLiteralExpr>(E)) {
5717+
// If it's a nil literal, just insert return. This is the only
5718+
// legal thing to return.
5719+
auto RS = new (Context) ReturnStmt(E->getStartLoc(), E);
5720+
BS->setElement(0, RS);
5721+
AFD->setHasSingleExpressionBody();
5722+
AFD->setSingleExpressionBody(E);
5723+
}
5724+
}
5725+
}
5726+
}
5727+
}
56765728
}
56775729

56785730
bool Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {

lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,37 @@ static void diagnoseMissingReturn(const UnreachableInst *UI,
4242
SILLocation FLoc = F->getLocation();
4343

4444
Type ResTy;
45+
BraceStmt *BS;
4546

4647
if (auto *FD = FLoc.getAsASTNode<FuncDecl>()) {
4748
ResTy = FD->getResultInterfaceType();
49+
BS = FD->getBody(/*canSynthesize=*/false);
4850
} else if (auto *CD = FLoc.getAsASTNode<ConstructorDecl>()) {
4951
ResTy = CD->getResultInterfaceType();
52+
BS = FD->getBody();
5053
} else if (auto *CE = FLoc.getAsASTNode<ClosureExpr>()) {
5154
ResTy = CE->getResultType();
55+
BS = CE->getBody();
5256
} else {
5357
llvm_unreachable("unhandled case in MissingReturn");
5458
}
5559

5660
SILLocation L = UI->getLoc();
5761
assert(L && ResTy);
62+
auto numElements = BS->getNumElements();
63+
if (numElements > 0) {
64+
auto element = BS->getElement(numElements - 1);
65+
if (auto expr = element.dyn_cast<Expr *>()) {
66+
if (expr->getType()->getCanonicalType() == ResTy->getCanonicalType()) {
67+
Context.Diags.diagnose(
68+
expr->getStartLoc(),
69+
diag::missing_return_last_expr, ResTy,
70+
FLoc.isASTNode<ClosureExpr>() ? 1 : 0)
71+
.fixItInsert(expr->getStartLoc(), "return ");
72+
return;
73+
}
74+
}
75+
}
5876
auto diagID = F->isNoReturnFunction() ? diag::missing_never_call
5977
: diag::missing_return;
6078
diagnose(Context,

lib/Sema/CSApply.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7685,10 +7685,18 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr,
76857685
if (hadError)
76867686
return nullptr;
76877687
}
7688-
7688+
7689+
// We are supposed to use contextual type only if it is present and
7690+
// this expression doesn't represent the implicit return of the single
7691+
// expression function which got deduced to be `Never`.
7692+
auto shouldCoerceToContextualType = [&]() {
7693+
return convertType && !(getType(result)->isUninhabited() &&
7694+
getContextualTypePurpose() == CTP_ReturnSingleExpr);
7695+
};
7696+
76897697
// If we're supposed to convert the expression to some particular type,
76907698
// do so now.
7691-
if (convertType) {
7699+
if (shouldCoerceToContextualType()) {
76927700
result = rewriter.coerceToType(result, convertType,
76937701
getConstraintLocator(expr));
76947702
if (!result)

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21022102
}
21032103
};
21042104
break;
2105+
case CTP_ReturnSingleExpr:
21052106
case CTP_ReturnStmt:
21062107
// Special case the "conversion to void" case.
21072108
if (contextualType->isVoid()) {

lib/Sema/CSSimplify.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,9 +2771,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
27712771
}
27722772

27732773
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
2774-
// literals.
2774+
// literals and expressions representing an implicit return type of the single
2775+
// expression functions.
27752776
if (auto elt = locator.last()) {
2776-
if (elt->getKind() == ConstraintLocator::ClosureResult) {
2777+
if (elt->getKind() == ConstraintLocator::ClosureResult ||
2778+
elt->getKind() == ConstraintLocator::SingleExprFuncResultType) {
27772779
if (kind >= ConstraintKind::Subtype &&
27782780
(type1->isUninhabited() || type2->isVoid())) {
27792781
increaseScore(SK_FunctionConversion);

lib/Sema/CSSolver.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,9 @@ ConstraintSystem::solveImpl(Expr *&expr,
11691169
constraintKind = ConstraintKind::Bind;
11701170

11711171
auto *convertTypeLocator = getConstraintLocator(
1172-
getConstraintLocator(expr), ConstraintLocator::ContextualType);
1172+
expr, getContextualTypePurpose() == CTP_ReturnSingleExpr
1173+
? ConstraintLocator::SingleExprFuncResultType
1174+
: ConstraintLocator::ContextualType);
11731175

11741176
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
11751177
convertType = convertType.transform([&](Type type) -> Type {

lib/Sema/ConstraintLocator.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
8383
case KeyPathRoot:
8484
case KeyPathValue:
8585
case KeyPathComponentResult:
86+
case SingleExprFuncResultType:
8687
if (unsigned numValues = numNumericValuesInPathElement(elt.getKind())) {
8788
id.AddInteger(elt.getValue());
8889
if (numValues > 1)
@@ -365,8 +366,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
365366
case KeyPathComponentResult:
366367
out << "key path component result";
367368
break;
369+
case SingleExprFuncResultType:
370+
out << " expected result type of the function with a single expression";
371+
break;
368372
}
369373
}
370-
371374
out << ']';
372375
}

lib/Sema/ConstraintLocator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ class ConstraintLocator : public llvm::FoldingSetNode {
137137
KeyPathValue,
138138
/// The result type of a key path component. Not used for subscripts.
139139
KeyPathComponentResult,
140+
/// The expected type of the function with a single expression body.
141+
SingleExprFuncResultType,
140142
};
141143

142144
/// Determine the number of numeric values used for the given path
@@ -171,6 +173,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
171173
case KeyPathRoot:
172174
case KeyPathValue:
173175
case KeyPathComponentResult:
176+
case SingleExprFuncResultType:
174177
return 0;
175178

176179
case OpenedGeneric:
@@ -241,6 +244,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
241244
case KeyPathRoot:
242245
case KeyPathValue:
243246
case KeyPathComponentResult:
247+
case SingleExprFuncResultType:
244248
return 0;
245249

246250
case FunctionArgument:

lib/Sema/TypeCheckStmt.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "TypeChecker.h"
1818
#include "TypeCheckType.h"
1919
#include "MiscDiagnostics.h"
20+
#include "ConstraintSystem.h"
2021
#include "swift/Subsystems.h"
2122
#include "swift/AST/ASTPrinter.h"
2223
#include "swift/AST/ASTWalker.h"
@@ -512,10 +513,17 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
512513
}
513514
}
514515

516+
ContextualTypePurpose ctp = CTP_ReturnStmt;
517+
if (auto func =
518+
dyn_cast_or_null<FuncDecl>(TheFunc->getAbstractFunctionDecl())) {
519+
if (func->hasSingleExpressionBody()) {
520+
ctp = CTP_ReturnSingleExpr;
521+
}
522+
}
523+
515524
auto exprTy = TC.typeCheckExpression(E, DC, TypeLoc::withoutLoc(ResultTy),
516-
CTP_ReturnStmt,
525+
ctp,
517526
options);
518-
519527
RS->setResult(E);
520528

521529
if (!exprTy) {
@@ -1926,10 +1934,37 @@ bool TypeChecker::typeCheckFunctionBodyUntil(FuncDecl *FD,
19261934
BraceStmt *BS = FD->getBody();
19271935
assert(BS && "Should have a body");
19281936

1937+
if (FD->hasSingleExpressionBody()) {
1938+
auto resultTypeLoc = FD->getBodyResultTypeLoc();
1939+
auto E = FD->getSingleExpressionBody();
1940+
1941+
if (resultTypeLoc.isNull() || resultTypeLoc.getType()->isVoid()) {
1942+
// The function returns void. We don't need an explicit return, no matter
1943+
// what the type of the expression is. Take the inserted return back out.
1944+
BS->setElement(0, E);
1945+
}
1946+
}
1947+
19291948
StmtChecker SC(*this, static_cast<AbstractFunctionDecl *>(FD));
19301949
SC.EndTypeCheckLoc = EndTypeCheckLoc;
19311950
bool HadError = SC.typeCheckBody(BS);
19321951

1952+
// If this was a function with a single expression body, let's see
1953+
// if implicit return statement came out to be `Never` which means
1954+
// that we have eagerly converted something like `{ fatalError() }`
1955+
// into `{ return fatalError() }` that has to be corrected here.
1956+
if (FD->hasSingleExpressionBody()) {
1957+
if (auto *stmt = BS->getElement(0).dyn_cast<Stmt *>()) {
1958+
if (auto *RS = dyn_cast<ReturnStmt>(stmt)) {
1959+
if (RS->isImplicit() && RS->hasResult()) {
1960+
auto returnType = RS->getResult()->getType();
1961+
if (returnType && returnType->isUninhabited())
1962+
BS->setElement(0, RS->getResult());
1963+
}
1964+
}
1965+
}
1966+
}
1967+
19331968
FD->setBody(BS);
19341969
return HadError;
19351970
}

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ enum ContextualTypePurpose {
202202
CTP_Unused, ///< No contextual type is specified.
203203
CTP_Initialization, ///< Pattern binding initialization.
204204
CTP_ReturnStmt, ///< Value specified to a 'return' statement.
205+
CTP_ReturnSingleExpr, ///< Value implicitly returned from a function.
205206
CTP_YieldByValue, ///< By-value yield operand.
206207
CTP_YieldByReference, ///< By-reference yield operand.
207208
CTP_ThrowStmt, ///< Value specified to a 'throw' statement.

0 commit comments

Comments
 (0)