Skip to content

Commit 49fe7f7

Browse files
authored
Merge pull request #68653 from hamishknight/nestless-5.9
[5.9] [Sema] Catch invalid if/switch exprs in more places
2 parents abe28dc + 5e7b70b commit 49fe7f7

14 files changed

+428
-92
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
@@ -3850,7 +3850,20 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
38503850
llvm::DenseSet<SingleValueStmtExpr *> ValidSingleValueStmtExprs;
38513851

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

38553868
private:
38563869
/// Mark a given expression as a valid position for a SingleValueStmtExpr.
@@ -3862,8 +3875,23 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
38623875
ValidSingleValueStmtExprs.insert(SVE);
38633876
}
38643877

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

38693897
AssignExpr *findAssignment(Expr *E) const {
@@ -3989,28 +4017,33 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
39894017
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
39904018
for (auto idx : range(PBD->getNumPatternEntries()))
39914019
markValidSingleValueStmt(PBD->getInit(idx));
4020+
4021+
return Action::Continue();
39924022
}
3993-
// Valid as a single expression body of a function. This is needed in
3994-
// addition to ReturnStmt checking, as we will remove the return if the
3995-
// expression is inferred to be Never.
3996-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
3997-
if (AFD->hasSingleExpressionBody())
3998-
markValidSingleValueStmt(AFD->getSingleExpressionBody());
3999-
}
4000-
return Action::Continue();
4023+
// We don't want to walk into any other decl, we will visit them as part of
4024+
// typeCheckDecl.
4025+
return Action::SkipChildren();
40014026
}
40024027
};
40034028
} // end anonymous namespace
40044029

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

40164049
/// Perform diagnostics for func/init/deinit declarations.
@@ -4026,10 +4059,6 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
40264059
auto &ctx = AFD->getDeclContext()->getASTContext();
40274060
VarDeclUsageChecker checker(AFD, ctx.Diags);
40284061
AFD->walk(checker);
4029-
4030-
// Do a similar walk to check for out of place SingleValueStmtExprs.
4031-
SingleValueStmtUsageChecker sveChecker(ctx);
4032-
AFD->walk(sveChecker);
40334062
}
40344063

40354064
auto *body = AFD->getBody();
@@ -5864,10 +5893,10 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E,
58645893
//===----------------------------------------------------------------------===//
58655894

58665895
/// Emit diagnostics for syntactic restrictions on a given expression.
5867-
void swift::performSyntacticExprDiagnostics(const Expr *E,
5868-
const DeclContext *DC,
5869-
bool isExprStmt,
5870-
bool disableExprAvailabilityChecking) {
5896+
void swift::performSyntacticExprDiagnostics(
5897+
const Expr *E, const DeclContext *DC,
5898+
llvm::Optional<ContextualTypePurpose> contextualPurpose, bool isExprStmt,
5899+
bool disableExprAvailabilityChecking, bool disableOutOfPlaceExprChecking) {
58715900
auto &ctx = DC->getASTContext();
58725901
TypeChecker::diagnoseSelfAssignment(E);
58735902
diagSyntacticUseRestrictions(E, DC, isExprStmt);
@@ -5886,6 +5915,8 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
58865915
diagnoseConstantArgumentRequirement(E, DC);
58875916
diagUnqualifiedAccessToMethodNamedSelf(E, DC);
58885917
diagnoseDictionaryLiteralDuplicateKeyEntries(E, DC);
5918+
if (!disableOutOfPlaceExprChecking)
5919+
diagnoseOutOfPlaceExprs(ctx, const_cast<Expr *>(E), contextualPurpose);
58895920
}
58905921

58915922
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
@@ -299,7 +299,11 @@ class FunctionSyntacticDiagnosticWalker : public ASTWalker {
299299
}
300300

301301
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
302-
performSyntacticExprDiagnostics(expr, dcStack.back(), /*isExprStmt=*/false);
302+
// We skip out-of-place expr checking here since we've already performed it.
303+
performSyntacticExprDiagnostics(expr, dcStack.back(), /*ctp*/ llvm::None,
304+
/*isExprStmt=*/false,
305+
/*disableAvailabilityChecking*/ false,
306+
/*disableOutOfPlaceExprChecking*/ true);
303307

304308
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
305309
if (closure->isSeparatelyTypeChecked()) {
@@ -346,8 +350,9 @@ void constraints::performSyntacticDiagnosticsForTarget(
346350
switch (target.kind) {
347351
case SyntacticElementTarget::Kind::expression: {
348352
// First emit diagnostics for the main expression.
349-
performSyntacticExprDiagnostics(target.getAsExpr(), dc,
350-
isExprStmt, disableExprAvailabilityChecking);
353+
performSyntacticExprDiagnostics(
354+
target.getAsExpr(), dc, target.getExprContextualTypePurpose(),
355+
isExprStmt, disableExprAvailabilityChecking);
351356
return;
352357
}
353358

@@ -356,17 +361,25 @@ void constraints::performSyntacticDiagnosticsForTarget(
356361

357362
// First emit diagnostics for the main expression.
358363
performSyntacticExprDiagnostics(stmt->getTypeCheckedSequence(), dc,
359-
isExprStmt,
364+
CTP_ForEachSequence, isExprStmt,
360365
disableExprAvailabilityChecking);
361366

362367
if (auto *whereExpr = stmt->getWhere())
363-
performSyntacticExprDiagnostics(whereExpr, dc, /*isExprStmt*/ false);
368+
performSyntacticExprDiagnostics(whereExpr, dc, CTP_Condition,
369+
/*isExprStmt*/ false);
364370
return;
365371
}
366372

367373
case SyntacticElementTarget::Kind::function: {
374+
// Check for out of place expressions. This needs to be done on the entire
375+
// function body rather than on individual expressions since we need the
376+
// context of the parent nodes.
377+
auto *body = target.getFunctionBody();
378+
diagnoseOutOfPlaceExprs(dc->getASTContext(), body,
379+
/*contextualPurpose*/ llvm::None);
380+
368381
FunctionSyntacticDiagnosticWalker walker(dc);
369-
target.getFunctionBody()->walk(walker);
382+
body->walk(walker);
370383
return;
371384
}
372385
case SyntacticElementTarget::Kind::closure:

test/Constraints/closures.swift

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

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)