Skip to content

Commit 8518136

Browse files
authored
Merge pull request #34810 from xedin/rdar-66709164
[ConstraintSystem] Detect and diagnose extraneous return(s) in result build body
2 parents 46f53a0 + 8b49e35 commit 8518136

File tree

8 files changed

+107
-8
lines changed

8 files changed

+107
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5482,7 +5482,10 @@ NOTE(previous_result_builder_here, none,
54825482
"previous result builder specified here", ())
54835483
ERROR(result_builder_arguments, none,
54845484
"result builder attributes cannot have arguments", ())
5485-
WARNING(result_builder_disabled_by_return, none,
5485+
ERROR(result_builder_disabled_by_return, none,
5486+
"cannot use explicit 'return' statement in the body of result builder %0",
5487+
(Type))
5488+
WARNING(result_builder_disabled_by_return_warn, none,
54865489
"application of result builder %0 disabled by explicit 'return' "
54875490
"statement", (Type))
54885491
NOTE(result_builder_remove_attr, none,

include/swift/Sema/CSFix.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2028,7 +2028,8 @@ class AllowKeyPathWithoutComponents final : public ConstraintFix {
20282028
ConstraintLocator *locator);
20292029
};
20302030

2031-
class IgnoreInvalidResultBuilderBody final : public ConstraintFix {
2031+
class IgnoreInvalidResultBuilderBody : public ConstraintFix {
2032+
protected:
20322033
enum class ErrorInPhase {
20332034
PreCheck,
20342035
ConstraintGeneration,
@@ -2067,6 +2068,22 @@ class IgnoreInvalidResultBuilderBody final : public ConstraintFix {
20672068
create(ConstraintSystem &cs, ErrorInPhase phase, ConstraintLocator *locator);
20682069
};
20692070

2071+
class IgnoreResultBuilderWithReturnStmts final
2072+
: public IgnoreInvalidResultBuilderBody {
2073+
Type BuilderType;
2074+
2075+
IgnoreResultBuilderWithReturnStmts(ConstraintSystem &cs, Type builderTy,
2076+
ConstraintLocator *locator)
2077+
: IgnoreInvalidResultBuilderBody(cs, ErrorInPhase::PreCheck, locator),
2078+
BuilderType(builderTy) {}
2079+
2080+
public:
2081+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2082+
2083+
static IgnoreResultBuilderWithReturnStmts *
2084+
create(ConstraintSystem &cs, Type builderTy, ConstraintLocator *locator);
2085+
};
2086+
20702087
class SpecifyContextualTypeForNil final : public ConstraintFix {
20712088
SpecifyContextualTypeForNil(ConstraintSystem &cs,
20722089
ConstraintLocator *locator)

lib/Sema/BuilderTransform.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,7 +1554,7 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
15541554

15551555
ctx.Diags.diagnose(
15561556
returnStmts.front()->getReturnLoc(),
1557-
diag::result_builder_disabled_by_return, builderType);
1557+
diag::result_builder_disabled_by_return_warn, builderType);
15581558

15591559
// Note that one can remove the result builder attribute.
15601560
auto attr = func->getAttachedResultBuilder();
@@ -1671,7 +1671,7 @@ ConstraintSystem::matchResultBuilder(
16711671
if (InvalidResultBuilderBodies.count(fn)) {
16721672
(void)recordFix(
16731673
IgnoreInvalidResultBuilderBody::duringConstraintGeneration(
1674-
*this, getConstraintLocator(fn.getBody())));
1674+
*this, getConstraintLocator(fn.getAbstractClosureExpr())));
16751675
return getTypeMatchSuccess();
16761676
}
16771677

@@ -1690,13 +1690,25 @@ ConstraintSystem::matchResultBuilder(
16901690
return getTypeMatchFailure(locator);
16911691

16921692
if (recordFix(IgnoreInvalidResultBuilderBody::duringPreCheck(
1693-
*this, getConstraintLocator(fn.getBody()))))
1693+
*this, getConstraintLocator(fn.getAbstractClosureExpr()))))
16941694
return getTypeMatchFailure(locator);
16951695

16961696
return getTypeMatchSuccess();
16971697
}
16981698

16991699
case ResultBuilderBodyPreCheck::HasReturnStmt:
1700+
// Diagnostic mode means that solver couldn't reach any viable
1701+
// solution, so let's diagnose presence of a `return` statement
1702+
// in the closure body.
1703+
if (shouldAttemptFixes()) {
1704+
if (recordFix(IgnoreResultBuilderWithReturnStmts::create(
1705+
*this, builderType,
1706+
getConstraintLocator(fn.getAbstractClosureExpr()))))
1707+
return getTypeMatchFailure(locator);
1708+
1709+
return getTypeMatchSuccess();
1710+
}
1711+
17001712
// If the body has a return statement, suppress the transform but
17011713
// continue solving the constraint system.
17021714
return None;
@@ -1744,7 +1756,7 @@ ConstraintSystem::matchResultBuilder(
17441756

17451757
if (recordFix(
17461758
IgnoreInvalidResultBuilderBody::duringConstraintGeneration(
1747-
*this, getConstraintLocator(fn.getBody()))))
1759+
*this, getConstraintLocator(fn.getAbstractClosureExpr()))))
17481760
return getTypeMatchFailure(locator);
17491761

17501762
return getTypeMatchSuccess();

lib/Sema/CSBindings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
14541454
// If the whole body is being ignored due to a pre-check failure,
14551455
// let's not record a fix about result type since there is
14561456
// just not enough context to infer it without a body.
1457-
if (cs.hasFixFor(cs.getConstraintLocator(closure->getBody()),
1457+
if (cs.hasFixFor(cs.getConstraintLocator(closure),
14581458
FixKind::IgnoreInvalidResultBuilderBody))
14591459
return None;
14601460

lib/Sema/CSDiagnostics.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7070,3 +7070,22 @@ bool ReferenceToInvalidDeclaration::diagnoseAsError() {
70707070
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
70717071
return true;
70727072
}
7073+
7074+
bool InvalidReturnInResultBuilderBody::diagnoseAsError() {
7075+
auto *closure = castToExpr<ClosureExpr>(getAnchor());
7076+
7077+
auto returnStmts = TypeChecker::findReturnStatements(closure);
7078+
assert(!returnStmts.empty());
7079+
7080+
auto loc = returnStmts.front()->getReturnLoc();
7081+
emitDiagnosticAt(loc, diag::result_builder_disabled_by_return, BuilderType);
7082+
7083+
// Note that one can remove all of the return statements.
7084+
{
7085+
auto diag = emitDiagnosticAt(loc, diag::result_builder_remove_returns);
7086+
for (auto returnStmt : returnStmts)
7087+
diag.fixItRemove(returnStmt->getReturnLoc());
7088+
}
7089+
7090+
return true;
7091+
}

lib/Sema/CSDiagnostics.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,26 @@ class ReferenceToInvalidDeclaration final : public FailureDiagnostic {
23112311
bool diagnoseAsError() override;
23122312
};
23132313

2314+
/// Diagnose use of `return` statements in a body of a result builder.
2315+
///
2316+
/// \code
2317+
/// struct S : Builder {
2318+
/// var foo: some Builder {
2319+
/// return EmptyBuilder()
2320+
/// }
2321+
/// }
2322+
/// \endcode
2323+
class InvalidReturnInResultBuilderBody final : public FailureDiagnostic {
2324+
Type BuilderType;
2325+
2326+
public:
2327+
InvalidReturnInResultBuilderBody(const Solution &solution, Type builderTy,
2328+
ConstraintLocator *locator)
2329+
: FailureDiagnostic(solution, locator), BuilderType(builderTy) {}
2330+
2331+
bool diagnoseAsError() override;
2332+
};
2333+
23142334
} // end namespace constraints
23152335
} // end namespace swift
23162336

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,7 @@ bool IgnoreInvalidResultBuilderBody::diagnose(const Solution &solution,
15801580
return true; // Already diagnosed by `matchResultBuilder`.
15811581
}
15821582

1583-
auto *S = getAnchor().get<Stmt *>();
1583+
auto *S = castToExpr<ClosureExpr>(getAnchor())->getBody();
15841584

15851585
class PreCheckWalker : public ASTWalker {
15861586
DeclContext *DC;
@@ -1645,3 +1645,16 @@ AllowRefToInvalidDecl::create(ConstraintSystem &cs,
16451645
ConstraintLocator *locator) {
16461646
return new (cs.getAllocator()) AllowRefToInvalidDecl(cs, locator);
16471647
}
1648+
1649+
bool IgnoreResultBuilderWithReturnStmts::diagnose(const Solution &solution,
1650+
bool asNote) const {
1651+
InvalidReturnInResultBuilderBody failure(solution, BuilderType, getLocator());
1652+
return failure.diagnose(asNote);
1653+
}
1654+
1655+
IgnoreResultBuilderWithReturnStmts *
1656+
IgnoreResultBuilderWithReturnStmts::create(ConstraintSystem &cs, Type builderTy,
1657+
ConstraintLocator *locator) {
1658+
return new (cs.getAllocator())
1659+
IgnoreResultBuilderWithReturnStmts(cs, builderTy, locator);
1660+
}

test/Constraints/result_builder_diags.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,21 @@ struct MyTuplifiedStruct {
286286
}
287287
}
288288

289+
func test_invalid_return_type_in_body() {
290+
tuplify(true) { _ -> (Void, Int) in
291+
tuplify(false) { condition in
292+
if condition {
293+
return 42 // expected-error {{cannot use explicit 'return' statement in the body of result builder 'TupleBuilder'}}
294+
// expected-note@-1 {{remove 'return' statements to apply the result builder}} {{9-16=}}
295+
} else {
296+
1
297+
}
298+
}
299+
300+
42
301+
}
302+
}
303+
289304
// Check that we're performing syntactic use diagnostics.
290305
func acceptMetatype<T>(_: T.Type) -> Bool { true }
291306

0 commit comments

Comments
 (0)