Skip to content

Commit 053f698

Browse files
authored
Merge pull request #68207 from hamishknight/nestless
2 parents de4370b + 94cf562 commit 053f698

14 files changed

+421
-90
lines changed

lib/AST/Expr.cpp

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,30 +2519,47 @@ SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
25192519

25202520
SingleValueStmtExpr *
25212521
SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(Expr *E) {
2522-
while (true) {
2523-
// Look through implicit conversions.
2524-
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(E)) {
2525-
E = ICE->getSubExpr();
2526-
continue;
2522+
class SVEFinder final : public ASTWalker {
2523+
public:
2524+
SingleValueStmtExpr *FoundSVE = nullptr;
2525+
2526+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
2527+
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E)) {
2528+
FoundSVE = SVE;
2529+
return Action::Stop();
2530+
}
2531+
2532+
// Look through implicit exprs.
2533+
if (E->isImplicit())
2534+
return Action::Continue(E);
2535+
2536+
// Look through coercions.
2537+
if (isa<CoerceExpr>(E))
2538+
return Action::Continue(E);
2539+
2540+
// Look through try/await (this is invalid, but we'll error on it in
2541+
// effect checking).
2542+
if (isa<AnyTryExpr>(E) || isa<AwaitExpr>(E))
2543+
return Action::Continue(E);
2544+
2545+
return Action::Stop();
25272546
}
2528-
// Look through coercions.
2529-
if (auto *CE = dyn_cast<CoerceExpr>(E)) {
2530-
E = CE->getSubExpr();
2531-
continue;
2547+
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
2548+
return Action::Stop();
25322549
}
2533-
// Look through try/await (this is invalid, but we'll error on it in
2534-
// effect checking).
2535-
if (auto *TE = dyn_cast<AnyTryExpr>(E)) {
2536-
E = TE->getSubExpr();
2537-
continue;
2550+
PreWalkAction walkToDeclPre(Decl *D) override {
2551+
return Action::Stop();
25382552
}
2539-
if (auto *AE = dyn_cast<AwaitExpr>(E)) {
2540-
E = AE->getSubExpr();
2541-
continue;
2553+
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
2554+
return Action::Stop();
25422555
}
2543-
break;
2544-
}
2545-
return dyn_cast<SingleValueStmtExpr>(E);
2556+
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
2557+
return Action::Stop();
2558+
}
2559+
};
2560+
SVEFinder finder;
2561+
E->walk(finder);
2562+
return finder.FoundSVE;
25462563
}
25472564

25482565
SourceRange SingleValueStmtExpr::getSourceRange() const {

lib/Sema/MiscDiagnostics.cpp

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3848,7 +3848,20 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
38483848
llvm::DenseSet<SingleValueStmtExpr *> ValidSingleValueStmtExprs;
38493849

38503850
public:
3851-
SingleValueStmtUsageChecker(ASTContext &ctx) : Ctx(ctx), Diags(ctx.Diags) {}
3851+
SingleValueStmtUsageChecker(
3852+
ASTContext &ctx, ASTNode root,
3853+
llvm::Optional<ContextualTypePurpose> contextualPurpose)
3854+
: Ctx(ctx), Diags(ctx.Diags) {
3855+
assert(!root.is<Expr *>() || contextualPurpose &&
3856+
"Must provide contextual purpose for expr");
3857+
3858+
// If we have a contextual purpose, this is for an expression. Check if it's
3859+
// an expression in a valid position.
3860+
if (contextualPurpose) {
3861+
markAnyValidTopLevelSingleValueStmt(root.get<Expr *>(),
3862+
*contextualPurpose);
3863+
}
3864+
}
38523865

38533866
private:
38543867
/// Mark a given expression as a valid position for a SingleValueStmtExpr.
@@ -3860,8 +3873,23 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
38603873
ValidSingleValueStmtExprs.insert(SVE);
38613874
}
38623875

3876+
/// Mark a valid top-level expression with a given contextual purpose.
3877+
void markAnyValidTopLevelSingleValueStmt(Expr *E, ContextualTypePurpose ctp) {
3878+
// Allowed in returns, throws, and bindings.
3879+
switch (ctp) {
3880+
case CTP_ReturnStmt:
3881+
case CTP_ReturnSingleExpr:
3882+
case CTP_ThrowStmt:
3883+
case CTP_Initialization:
3884+
markValidSingleValueStmt(E);
3885+
break;
3886+
default:
3887+
break;
3888+
}
3889+
}
3890+
38633891
MacroWalking getMacroWalkingBehavior() const override {
3864-
return MacroWalking::Expansion;
3892+
return MacroWalking::ArgumentsAndExpansion;
38653893
}
38663894

38673895
AssignExpr *findAssignment(Expr *E) const {
@@ -3987,28 +4015,33 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
39874015
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
39884016
for (auto idx : range(PBD->getNumPatternEntries()))
39894017
markValidSingleValueStmt(PBD->getInit(idx));
4018+
4019+
return Action::Continue();
39904020
}
3991-
// Valid as a single expression body of a function. This is needed in
3992-
// addition to ReturnStmt checking, as we will remove the return if the
3993-
// expression is inferred to be Never.
3994-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
3995-
if (AFD->hasSingleExpressionBody())
3996-
markValidSingleValueStmt(AFD->getSingleExpressionBody());
3997-
}
3998-
return Action::Continue();
4021+
// We don't want to walk into any other decl, we will visit them as part of
4022+
// typeCheckDecl.
4023+
return Action::SkipChildren();
39994024
}
40004025
};
40014026
} // end anonymous namespace
40024027

4028+
void swift::diagnoseOutOfPlaceExprs(
4029+
ASTContext &ctx, ASTNode root,
4030+
llvm::Optional<ContextualTypePurpose> contextualPurpose) {
4031+
// TODO: We ought to consider moving this into pre-checking such that we can
4032+
// still diagnose on invalid code, and don't have to traverse over implicit
4033+
// exprs. We need to first separate out SequenceExpr folding though.
4034+
SingleValueStmtUsageChecker sveChecker(ctx, root, contextualPurpose);
4035+
root.walk(sveChecker);
4036+
}
4037+
40034038
/// Apply the warnings managed by VarDeclUsageChecker to the top level
40044039
/// code declarations that haven't been checked yet.
40054040
void swift::
40064041
performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) {
40074042
auto &ctx = TLCD->getDeclContext()->getASTContext();
40084043
VarDeclUsageChecker checker(TLCD, ctx.Diags);
40094044
TLCD->walk(checker);
4010-
SingleValueStmtUsageChecker sveChecker(ctx);
4011-
TLCD->walk(sveChecker);
40124045
}
40134046

40144047
/// Perform diagnostics for func/init/deinit declarations.
@@ -4024,10 +4057,6 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
40244057
auto &ctx = AFD->getDeclContext()->getASTContext();
40254058
VarDeclUsageChecker checker(AFD, ctx.Diags);
40264059
AFD->walk(checker);
4027-
4028-
// Do a similar walk to check for out of place SingleValueStmtExprs.
4029-
SingleValueStmtUsageChecker sveChecker(ctx);
4030-
AFD->walk(sveChecker);
40314060
}
40324061

40334062
auto *body = AFD->getBody();
@@ -5849,10 +5878,10 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E,
58495878
//===----------------------------------------------------------------------===//
58505879

58515880
/// Emit diagnostics for syntactic restrictions on a given expression.
5852-
void swift::performSyntacticExprDiagnostics(const Expr *E,
5853-
const DeclContext *DC,
5854-
bool isExprStmt,
5855-
bool disableExprAvailabilityChecking) {
5881+
void swift::performSyntacticExprDiagnostics(
5882+
const Expr *E, const DeclContext *DC,
5883+
llvm::Optional<ContextualTypePurpose> contextualPurpose, bool isExprStmt,
5884+
bool disableExprAvailabilityChecking, bool disableOutOfPlaceExprChecking) {
58565885
auto &ctx = DC->getASTContext();
58575886
TypeChecker::diagnoseSelfAssignment(E);
58585887
diagSyntacticUseRestrictions(E, DC, isExprStmt);
@@ -5871,6 +5900,8 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
58715900
diagnoseConstantArgumentRequirement(E, DC);
58725901
diagUnqualifiedAccessToMethodNamedSelf(E, DC);
58735902
diagnoseDictionaryLiteralDuplicateKeyEntries(E, DC);
5903+
if (!disableOutOfPlaceExprChecking)
5904+
diagnoseOutOfPlaceExprs(ctx, const_cast<Expr *>(E), contextualPurpose);
58745905
}
58755906

58765907
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {

lib/Sema/MiscDiagnostics.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace swift {
2828
class ApplyExpr;
2929
class CallExpr;
3030
class ClosureExpr;
31+
enum ContextualTypePurpose : uint8_t;
3132
class DeclContext;
3233
class Decl;
3334
class Expr;
@@ -37,10 +38,22 @@ namespace swift {
3738
class ValueDecl;
3839
class ForEachStmt;
3940

41+
/// Diagnose any expressions that appear in an unsupported position. If visiting
42+
/// an expression directly, its \p contextualPurpose should be provided to
43+
/// evaluate its position.
44+
void diagnoseOutOfPlaceExprs(
45+
ASTContext &ctx, ASTNode root,
46+
llvm::Optional<ContextualTypePurpose> contextualPurpose);
47+
4048
/// Emit diagnostics for syntactic restrictions on a given expression.
49+
///
50+
/// Note: \p contextualPurpose must be non-nil, unless
51+
/// \p disableOutOfPlaceExprChecking is set to \c true.
4152
void performSyntacticExprDiagnostics(
4253
const Expr *E, const DeclContext *DC,
43-
bool isExprStmt, bool disableExprAvailabilityChecking = false);
54+
llvm::Optional<ContextualTypePurpose> contextualPurpose,
55+
bool isExprStmt, bool disableExprAvailabilityChecking = false,
56+
bool disableOutOfPlaceExprChecking = false);
4457

4558
/// Emit diagnostics for a given statement.
4659
void performStmtDiagnostics(const Stmt *S, DeclContext *DC);

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,11 @@ class FunctionSyntacticDiagnosticWalker : public ASTWalker {
307307
}
308308

309309
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
310-
performSyntacticExprDiagnostics(expr, dcStack.back(), /*isExprStmt=*/false);
310+
// We skip out-of-place expr checking here since we've already performed it.
311+
performSyntacticExprDiagnostics(expr, dcStack.back(), /*ctp*/ llvm::None,
312+
/*isExprStmt=*/false,
313+
/*disableAvailabilityChecking*/ false,
314+
/*disableOutOfPlaceExprChecking*/ true);
311315

312316
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
313317
if (closure->isSeparatelyTypeChecked()) {
@@ -354,8 +358,9 @@ void constraints::performSyntacticDiagnosticsForTarget(
354358
switch (target.kind) {
355359
case SyntacticElementTarget::Kind::expression: {
356360
// First emit diagnostics for the main expression.
357-
performSyntacticExprDiagnostics(target.getAsExpr(), dc,
358-
isExprStmt, disableExprAvailabilityChecking);
361+
performSyntacticExprDiagnostics(
362+
target.getAsExpr(), dc, target.getExprContextualTypePurpose(),
363+
isExprStmt, disableExprAvailabilityChecking);
359364
return;
360365
}
361366

@@ -364,17 +369,25 @@ void constraints::performSyntacticDiagnosticsForTarget(
364369

365370
// First emit diagnostics for the main expression.
366371
performSyntacticExprDiagnostics(stmt->getTypeCheckedSequence(), dc,
367-
isExprStmt,
372+
CTP_ForEachSequence, isExprStmt,
368373
disableExprAvailabilityChecking);
369374

370375
if (auto *whereExpr = stmt->getWhere())
371-
performSyntacticExprDiagnostics(whereExpr, dc, /*isExprStmt*/ false);
376+
performSyntacticExprDiagnostics(whereExpr, dc, CTP_Condition,
377+
/*isExprStmt*/ false);
372378
return;
373379
}
374380

375381
case SyntacticElementTarget::Kind::function: {
382+
// Check for out of place expressions. This needs to be done on the entire
383+
// function body rather than on individual expressions since we need the
384+
// context of the parent nodes.
385+
auto *body = target.getFunctionBody();
386+
diagnoseOutOfPlaceExprs(dc->getASTContext(), body,
387+
/*contextualPurpose*/ llvm::None);
388+
376389
FunctionSyntacticDiagnosticWalker walker(dc);
377-
target.getFunctionBody()->walk(walker);
390+
body->walk(walker);
378391
return;
379392
}
380393
case SyntacticElementTarget::Kind::closure:

test/Constraints/closures.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,11 +1152,9 @@ struct R_76250381<Result, Failure: Error> {
11521152
// rdar://77022842 - crash due to a missing argument to a ternary operator
11531153
func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) {
11541154
if let a = argA ?? false, if let b = argB ?? {
1155-
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
1156-
// expected-error@-2 {{initializer for conditional binding must have Optional type, not 'Bool'}}
1157-
// expected-error@-3 {{cannot convert value of type '() -> ()' to expected argument type 'Bool?'}}
1158-
// expected-error@-4 {{cannot convert value of type 'Void' to expected condition type 'Bool'}}
1159-
// expected-error@-5 {{'if' must have an unconditional 'else' to be used as expression}}
1155+
// expected-error@-1 {{initializer for conditional binding must have Optional type, not 'Bool'}}
1156+
// expected-error@-2 {{cannot convert value of type '() -> ()' to expected argument type 'Bool?'}}
1157+
// expected-error@-3 {{cannot convert value of type 'Void' to expected condition type 'Bool'}}
11601158
} // expected-error {{expected '{' after 'if' condition}}
11611159
}
11621160

test/Constraints/if_expr.swift

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,6 @@ func testReturnMismatch() {
387387
let _ = if .random() {
388388
return 1 // expected-error {{unexpected non-void return value in void function}}
389389
// expected-note@-1 {{did you mean to add a return type?}}
390-
// expected-error@-2 {{cannot 'return' in 'if' when used as expression}}
391390
} else {
392391
0
393392
}
@@ -651,9 +650,46 @@ func builderWithBinding() -> Either<String, Int> {
651650
}
652651
}
653652

653+
@Builder
654+
func builderWithInvalidBinding() -> Either<String, Int> {
655+
let str = (if .random() { "a" } else { "b" })
656+
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
657+
if .random() {
658+
str
659+
} else {
660+
1
661+
}
662+
}
663+
664+
func takesBuilder(@Builder _ fn: () -> Either<String, Int>) {}
665+
666+
func builderClosureWithBinding() {
667+
takesBuilder {
668+
// Make sure the binding gets type-checked as an if expression, but the
669+
// other if block gets type-checked as a stmt.
670+
let str = if .random() { "a" } else { "b" }
671+
if .random() {
672+
str
673+
} else {
674+
1
675+
}
676+
}
677+
}
678+
679+
func builderClosureWithInvalidBinding() {
680+
takesBuilder {
681+
let str = (if .random() { "a" } else { "b" })
682+
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
683+
if .random() {
684+
str
685+
} else {
686+
1
687+
}
688+
}
689+
}
690+
654691
func builderInClosure() {
655-
func build(@Builder _ fn: () -> Either<String, Int>) {}
656-
build {
692+
takesBuilder {
657693
if .random() {
658694
""
659695
} else {

0 commit comments

Comments
 (0)