Skip to content

Commit 488aebf

Browse files
Merge pull request #24327 from nate-chandler/nate/omit-return-5.1
[5.1] Return elision SE-255
2 parents 43fd437 + 662238d commit 488aebf

33 files changed

+2162
-16
lines changed

include/swift/AST/Decl.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,8 @@ class alignas(1 << DeclAlignInBits) Decl {
400400
/// The ResilienceExpansion to use for default arguments.
401401
DefaultArgumentResilienceExpansion : 1
402402
);
403-
404-
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1+1,
403+
404+
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+8+1+1+1+1+1+1+1+1,
405405
/// \see AbstractFunctionDecl::BodyKind
406406
BodyKind : 3,
407407

@@ -428,7 +428,10 @@ class alignas(1 << DeclAlignInBits) Decl {
428428

429429
/// Whether this member was synthesized as part of a derived
430430
/// protocol conformance.
431-
Synthesized : 1
431+
Synthesized : 1,
432+
433+
/// Whether this member's body consists of a single expression.
434+
HasSingleExpressionBody : 1
432435
);
433436

434437
SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+2+1+1+2,
@@ -5433,13 +5436,25 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
54335436
Bits.AbstractFunctionDecl.DefaultArgumentResilienceExpansion =
54345437
unsigned(ResilienceExpansion::Maximal);
54355438
Bits.AbstractFunctionDecl.Synthesized = false;
5439+
Bits.AbstractFunctionDecl.HasSingleExpressionBody = false;
54365440
}
54375441

54385442
void setBodyKind(BodyKind K) {
54395443
Bits.AbstractFunctionDecl.BodyKind = unsigned(K);
54405444
}
54415445

54425446
public:
5447+
void setHasSingleExpressionBody(bool Has = true) {
5448+
Bits.AbstractFunctionDecl.HasSingleExpressionBody = Has;
5449+
}
5450+
5451+
bool hasSingleExpressionBody() const {
5452+
return Bits.AbstractFunctionDecl.HasSingleExpressionBody;
5453+
}
5454+
5455+
Expr *getSingleExpressionBody() const;
5456+
void setSingleExpressionBody(Expr *NewBody);
5457+
54435458
/// Returns the string for the base name, or "_" if this is unnamed.
54445459
StringRef getNameStr() const {
54455460
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
@@ -1069,7 +1069,10 @@ namespace {
10691069
Indent -= 2;
10701070
}
10711071
}
1072-
if (auto Body = D->getBody(/*canSynthesize=*/false)) {
1072+
if (D->hasSingleExpressionBody()) {
1073+
OS << '\n';
1074+
printRec(D->getSingleExpressionBody());
1075+
} else if (auto Body = D->getBody(/*canSynthesize=*/false)) {
10731076
OS << '\n';
10741077
printRec(Body, D->getASTContext());
10751078
}

lib/AST/Decl.cpp

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

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

lib/Parse/ParseDecl.cpp

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

56385638
ParserResult<BraceStmt> Body = parseBraceItemList(diag::invalid_diagnostic);
5639-
if (!Body.isNull())
5640-
AFD->setBody(Body.get());
5639+
if (!Body.isNull()) {
5640+
BraceStmt * BS = Body.get();
5641+
AFD->setBody(BS);
5642+
5643+
// If the body consists of a single expression, turn it into a return
5644+
// statement.
5645+
//
5646+
// But don't do this transformation during code completion, as the source
5647+
// may be incomplete and the type mismatch in return statement will just
5648+
// confuse the type checker.
5649+
if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) {
5650+
auto Element = BS->getElement(0);
5651+
if (auto *stmt = Element.dyn_cast<Stmt *>()) {
5652+
auto kind = AFD->getKind();
5653+
if (kind == DeclKind::Var || kind == DeclKind::Subscript ||
5654+
kind == DeclKind::Func ) {
5655+
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
5656+
if (!returnStmt->hasResult()) {
5657+
auto returnExpr = TupleExpr::createEmpty(Context,
5658+
SourceLoc(),
5659+
SourceLoc(),
5660+
/*implicit*/true);
5661+
returnStmt->setResult(returnExpr);
5662+
AFD->setHasSingleExpressionBody();
5663+
AFD->setSingleExpressionBody(returnExpr);
5664+
}
5665+
}
5666+
}
5667+
} else if (auto *E = Element.dyn_cast<Expr *>()) {
5668+
if (auto SE = dyn_cast<SequenceExpr>(E->getSemanticsProvidingExpr())) {
5669+
if (SE->getNumElements() > 1 && isa<AssignExpr>(SE->getElement(1))) {
5670+
// This is an assignment. We don't want to implicitly return
5671+
// it.
5672+
return;
5673+
}
5674+
}
5675+
if (auto F = dyn_cast<FuncDecl>(AFD)) {
5676+
auto RS = new (Context) ReturnStmt(SourceLoc(), E);
5677+
BS->setElement(0, RS);
5678+
AFD->setHasSingleExpressionBody();
5679+
AFD->setSingleExpressionBody(E);
5680+
} else if (auto *F = dyn_cast<ConstructorDecl>(AFD)) {
5681+
if (F->getFailability() != OTK_None && isa<NilLiteralExpr>(E)) {
5682+
// If it's a nil literal, just insert return. This is the only
5683+
// legal thing to return.
5684+
auto RS = new (Context) ReturnStmt(E->getStartLoc(), E);
5685+
BS->setElement(0, RS);
5686+
AFD->setHasSingleExpressionBody();
5687+
AFD->setSingleExpressionBody(E);
5688+
}
5689+
}
5690+
}
5691+
}
5692+
}
56415693
}
56425694

56435695
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
@@ -7721,10 +7721,18 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr,
77217721
if (hadError)
77227722
return nullptr;
77237723
}
7724-
7724+
7725+
// We are supposed to use contextual type only if it is present and
7726+
// this expression doesn't represent the implicit return of the single
7727+
// expression function which got deduced to be `Never`.
7728+
auto shouldCoerceToContextualType = [&]() {
7729+
return convertType && !(getType(result)->isUninhabited() &&
7730+
getContextualTypePurpose() == CTP_ReturnSingleExpr);
7731+
};
7732+
77257733
// If we're supposed to convert the expression to some particular type,
77267734
// do so now.
7727-
if (convertType) {
7735+
if (shouldCoerceToContextualType()) {
77287736
result = rewriter.coerceToType(result, convertType,
77297737
getConstraintLocator(expr));
77307738
if (!result)

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21572157
}
21582158
};
21592159
break;
2160+
case CTP_ReturnSingleExpr:
21602161
case CTP_ReturnStmt:
21612162
// Special case the "conversion to void" case.
21622163
if (contextualType->isVoid()) {

lib/Sema/CSSimplify.cpp

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

27852785
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
2786-
// literals.
2786+
// literals and expressions representing an implicit return type of the single
2787+
// expression functions.
27872788
if (auto elt = locator.last()) {
2788-
if (elt->getKind() == ConstraintLocator::ClosureResult) {
2789+
if (elt->getKind() == ConstraintLocator::ClosureResult ||
2790+
elt->getKind() == ConstraintLocator::SingleExprFuncResultType) {
27892791
if (kind >= ConstraintKind::Subtype &&
27902792
(type1->isUninhabited() || type2->isVoid())) {
27912793
increaseScore(SK_FunctionConversion);

lib/Sema/CSSolver.cpp

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

11341134
auto *convertTypeLocator = getConstraintLocator(
1135-
getConstraintLocator(expr), ConstraintLocator::ContextualType);
1135+
expr, getContextualTypePurpose() == CTP_ReturnSingleExpr
1136+
? ConstraintLocator::SingleExprFuncResultType
1137+
: ConstraintLocator::ContextualType);
11361138

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

lib/Sema/ConstraintLocator.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
8282
case KeyPathType:
8383
case KeyPathRoot:
8484
case KeyPathValue:
85+
case SingleExprFuncResultType:
8586
if (unsigned numValues = numNumericValuesInPathElement(elt.getKind())) {
8687
id.AddInteger(elt.getValue());
8788
if (numValues > 1)
@@ -360,8 +361,11 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
360361
case KeyPathValue:
361362
out << " keypath value";
362363
break;
364+
365+
case SingleExprFuncResultType:
366+
out << " expected result type of the function with a single expression";
367+
break;
363368
}
364369
}
365-
366370
out << ']';
367371
}

lib/Sema/ConstraintLocator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ class ConstraintLocator : public llvm::FoldingSetNode {
135135
KeyPathRoot,
136136
/// The value of a key path
137137
KeyPathValue,
138+
/// The expected type of the function with a single expression body.
139+
SingleExprFuncResultType,
138140
};
139141

140142
/// Determine the number of numeric values used for the given path
@@ -168,6 +170,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
168170
case KeyPathType:
169171
case KeyPathRoot:
170172
case KeyPathValue:
173+
case SingleExprFuncResultType:
171174
return 0;
172175

173176
case OpenedGeneric:
@@ -237,6 +240,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
237240
case KeyPathType:
238241
case KeyPathRoot:
239242
case KeyPathValue:
243+
case SingleExprFuncResultType:
240244
return 0;
241245

242246
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"
@@ -491,10 +492,17 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
491492
}
492493
}
493494

495+
ContextualTypePurpose ctp = CTP_ReturnStmt;
496+
if (auto func =
497+
dyn_cast_or_null<FuncDecl>(TheFunc->getAbstractFunctionDecl())) {
498+
if (func->hasSingleExpressionBody()) {
499+
ctp = CTP_ReturnSingleExpr;
500+
}
501+
}
502+
494503
auto exprTy = TC.typeCheckExpression(E, DC, TypeLoc::withoutLoc(ResultTy),
495-
CTP_ReturnStmt,
504+
ctp,
496505
options);
497-
498506
RS->setResult(E);
499507

500508
if (!exprTy) {
@@ -1920,10 +1928,37 @@ bool TypeChecker::typeCheckFunctionBodyUntil(FuncDecl *FD,
19201928
BraceStmt *BS = FD->getBody();
19211929
assert(BS && "Should have a body");
19221930

1931+
if (FD->hasSingleExpressionBody()) {
1932+
auto resultTypeLoc = FD->getBodyResultTypeLoc();
1933+
auto E = FD->getSingleExpressionBody();
1934+
1935+
if (resultTypeLoc.isNull() || resultTypeLoc.getType()->isVoid()) {
1936+
// The function returns void. We don't need an explicit return, no matter
1937+
// what the type of the expression is. Take the inserted return back out.
1938+
BS->setElement(0, E);
1939+
}
1940+
}
1941+
19231942
StmtChecker SC(*this, static_cast<AbstractFunctionDecl *>(FD));
19241943
SC.EndTypeCheckLoc = EndTypeCheckLoc;
19251944
bool HadError = SC.typeCheckBody(BS);
19261945

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

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)