Skip to content

Commit 4992c7a

Browse files
authored
Merge pull request #85538 from hamishknight/binding-fixes
[Sema] A couple of binding-related crasher fixes
2 parents 4234fb7 + ff833da commit 4992c7a

10 files changed

+108
-46
lines changed

lib/AST/ASTScopeCreation.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -959,11 +959,8 @@ AnnotatedInsertionPoint
959959
ConditionalClausePatternUseScope::expandAScopeThatCreatesANewInsertionPoint(
960960
ScopeCreator &scopeCreator) {
961961
auto *initializer = sec.getInitializer();
962-
if (!isa<ErrorExpr>(initializer)) {
963-
scopeCreator
964-
.constructExpandAndInsert<ConditionalClauseInitializerScope>(
965-
this, initializer);
966-
}
962+
scopeCreator.constructExpandAndInsert<ConditionalClauseInitializerScope>(
963+
this, initializer);
967964

968965
return {this,
969966
"Succeeding code must be in scope of conditional clause pattern bindings"};

lib/Sema/CSGen.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1322,7 +1322,18 @@ namespace {
13221322
}
13231323

13241324
virtual Type visitErrorExpr(ErrorExpr *E) {
1325-
return recordInvalidNode(E);
1325+
auto ty = recordInvalidNode(E);
1326+
// If we have an original expression, introduce a conversion to the hole
1327+
// type of the ErrorExpr. This avoids unnecessary diagnostics for the
1328+
// original expression in cases where the ErrorExpr could have provided
1329+
// contextual info, while also still allowing the original expression to
1330+
// be solved without holes being introduced prematurely, allowing e.g
1331+
// cursor info to work correctly.
1332+
if (auto *orig = E->getOriginalExpr()) {
1333+
CS.addConstraint(ConstraintKind::Conversion, CS.getType(orig), ty,
1334+
CS.getConstraintLocator(E));
1335+
}
1336+
return ty;
13261337
}
13271338

13281339
virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) {
@@ -3156,6 +3167,16 @@ namespace {
31563167
}
31573168

31583169
case PatternKind::Expr: {
3170+
// Make sure we invalidate any nested VarDecls early since generating
3171+
// constraints for a `where` clause may happen before we've generated
3172+
// constraints for the ExprPattern. We'll record a fix when visiting
3173+
// the UnresolvedPatternExpr.
3174+
// FIXME: We ought to use a conjunction for switch cases, then we
3175+
// wouldn't need this logic.
3176+
auto *EP = cast<ExprPattern>(pattern);
3177+
EP->getSubExpr()->forEachUnresolvedVariable([&](VarDecl *VD) {
3178+
CS.setType(VD, ErrorType::get(CS.getASTContext()));
3179+
});
31593180
// We generate constraints for ExprPatterns in a separate pass. For
31603181
// now, just create a type variable.
31613182
return setType(CS.createTypeVariable(CS.getConstraintLocator(locator),

lib/Sema/CSSimplify.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5582,6 +5582,10 @@ bool ConstraintSystem::repairFailures(
55825582
if (!anchor)
55835583
return false;
55845584

5585+
// If we have an ErrorExpr anchor, we don't need to do any more fixes.
5586+
if (isExpr<ErrorExpr>(anchor))
5587+
return true;
5588+
55855589
// This could be:
55865590
// - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`.
55875591
// - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are

lib/Sema/TypeCheckStmt.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,12 +1091,14 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
10911091
auto &eval = getASTContext().evaluator;
10921092
auto *S =
10931093
evaluateOrDefault(eval, PreCheckReturnStmtRequest{RS, DC}, nullptr);
1094+
if (!S)
1095+
return nullptr;
10941096

1095-
// We do a cast here as it may have been turned into a FailStmt. We should
1096-
// return that without doing anything else.
1097-
RS = dyn_cast_or_null<ReturnStmt>(S);
1097+
// We do a cast here as we may now have a different stmt (e.g a FailStmt, or
1098+
// a BraceStmt for error cases). If so, recurse into the visitor.
1099+
RS = dyn_cast<ReturnStmt>(S);
10981100
if (!RS)
1099-
return S;
1101+
return visit(S);
11001102

11011103
auto TheFunc = AnyFunctionRef::fromDeclContext(DC);
11021104
assert(TheFunc && "Should have bailed from pre-check if this is None");
@@ -1785,16 +1787,31 @@ Stmt *PreCheckReturnStmtRequest::evaluate(Evaluator &evaluator, ReturnStmt *RS,
17851787
auto &ctx = DC->getASTContext();
17861788
auto fn = AnyFunctionRef::fromDeclContext(DC);
17871789

1790+
auto errorResult = [&]() -> Stmt * {
1791+
// We don't need any recovery if there's no result, just bail.
1792+
if (!RS->hasResult())
1793+
return nullptr;
1794+
1795+
// If we have a result, make sure it's preserved, insert an implicit brace
1796+
// with a wrapping error expression. This ensures we can still do semantic
1797+
// functionality, and avoids downstream crashes where we expect the
1798+
// expression to have been type-checked.
1799+
auto *result = RS->getResult();
1800+
RS->setResult(nullptr);
1801+
auto *err = new (ctx) ErrorExpr(result->getSourceRange(), Type(), result);
1802+
return BraceStmt::createImplicit(ctx, {err});
1803+
};
1804+
17881805
// Not valid outside of a function.
17891806
if (!fn) {
17901807
ctx.Diags.diagnose(RS->getReturnLoc(), diag::return_invalid_outside_func);
1791-
return nullptr;
1808+
return errorResult();
17921809
}
17931810

17941811
// If the return is in a defer, then it isn't valid either.
17951812
if (isDefer(DC)) {
17961813
ctx.Diags.diagnose(RS->getReturnLoc(), diag::jump_out_of_defer, "return");
1797-
return nullptr;
1814+
return errorResult();
17981815
}
17991816

18001817
// The rest of the checks only concern return statements with results.
@@ -1815,8 +1832,7 @@ Stmt *PreCheckReturnStmtRequest::evaluate(Evaluator &evaluator, ReturnStmt *RS,
18151832
if (!nilExpr) {
18161833
ctx.Diags.diagnose(RS->getReturnLoc(), diag::return_init_non_nil)
18171834
.highlight(E->getSourceRange());
1818-
RS->setResult(nullptr);
1819-
return RS;
1835+
return errorResult();
18201836
}
18211837

18221838
// "return nil" is only permitted in a failable initializer.
@@ -1826,8 +1842,7 @@ Stmt *PreCheckReturnStmtRequest::evaluate(Evaluator &evaluator, ReturnStmt *RS,
18261842
ctx.Diags
18271843
.diagnose(ctor->getLoc(), diag::make_init_failable, ctor)
18281844
.fixItInsertAfter(ctor->getLoc(), "?");
1829-
RS->setResult(nullptr);
1830-
return RS;
1845+
return errorResult();
18311846
}
18321847

18331848
// Replace the "return nil" with a new 'fail' statement.

test/IDE/complete_return.swift

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,4 @@
1-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_VOID_1 | %FileCheck %s -check-prefix=RETURN_VOID_1
2-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_INT_1 | %FileCheck %s -check-prefix=RETURN_INT_1
3-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_INT_2 | %FileCheck %s -check-prefix=RETURN_INT_2
4-
5-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TRY_RETURN_INT | %FileCheck %s -check-prefix=RETURN_INT_1
6-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TRY_RETURN_VOID | %FileCheck %s -check-prefix=RETURN_VOID_1
7-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR1 | %FileCheck %s -check-prefix=RETURN_TR1
8-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR2 | %FileCheck %s -check-prefix=RETURN_TR2
9-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR3 | %FileCheck %s -check-prefix=RETURN_TR3
10-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR1_METHOD | %FileCheck %s -check-prefix=RETURN_TR1
11-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR2_METHOD | %FileCheck %s -check-prefix=RETURN_TR2
12-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR3_METHOD | %FileCheck %s -check-prefix=RETURN_TR3
13-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR1_STATICMETHOD | %FileCheck %s -check-prefix=RETURN_TR1
14-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR2_STATICMETHOD | %FileCheck %s -check-prefix=RETURN_TR2
15-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR3_STATICMETHOD | %FileCheck %s -check-prefix=RETURN_TR3
16-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR1_CLOSURE | %FileCheck %s -check-prefix=RETURN_TR1
17-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR2_CLOSURE | %FileCheck %s -check-prefix=RETURN_TR2
18-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RETURN_TR3_CLOSURE | %FileCheck %s -check-prefix=RETURN_TR3
1+
// RUN: %batch-code-completion
192

203
struct FooStruct {
214
var instanceVar : Int
@@ -64,11 +47,11 @@ func testReturnInt2(_ fooObject: FooStruct) {
6447
}
6548

6649
func testMisplacedTry() throws -> Int {
67-
try return #^TRY_RETURN_INT^#
50+
try return #^TRY_RETURN_INT?check=RETURN_INT_1^#
6851
}
6952

7053
func testMisplacedTryVoid() throws {
71-
try return #^TRY_RETURN_VOID^#
54+
try return #^TRY_RETURN_VOID?check=RETURN_VOID_1^#
7255
}
7356

7457
func testTR1() -> Int? {
@@ -111,26 +94,26 @@ struct TestStruct {
11194
var i : Int
11295
var oi : Int?
11396
var fs : FooStruct
114-
return #^RETURN_TR1_METHOD^#
97+
return #^RETURN_TR1_METHOD?check=RETURN_TR1^#
11598
}
11699
func testTR2_method(_ g : Gen) -> Int? {
117-
return g.#^RETURN_TR2_METHOD^#
100+
return g.#^RETURN_TR2_METHOD?check=RETURN_TR2^#
118101
}
119102
func testTR3_method(_ g : Gen) -> Int? {
120-
return g.IG.#^RETURN_TR3_METHOD^#
103+
return g.IG.#^RETURN_TR3_METHOD?check=RETURN_TR3^#
121104
}
122105

123106
static func testTR1_static() -> Int? {
124107
var i : Int
125108
var oi : Int?
126109
var fs : FooStruct
127-
return #^RETURN_TR1_STATICMETHOD^#
110+
return #^RETURN_TR1_STATICMETHOD?check=RETURN_TR1^#
128111
}
129112
static func testTR2_static(_ g : Gen) -> Int? {
130-
return g.#^RETURN_TR2_STATICMETHOD^#
113+
return g.#^RETURN_TR2_STATICMETHOD?check=RETURN_TR2^#
131114
}
132115
static func testTR3_static(_ g : Gen) -> Int? {
133-
return g.IG.#^RETURN_TR3_STATICMETHOD^#
116+
return g.IG.#^RETURN_TR3_STATICMETHOD?check=RETURN_TR3^#
134117
}
135118
}
136119

@@ -140,12 +123,26 @@ func testClosures(_ g: Gen) {
140123
var fs : FooStruct
141124

142125
_ = { () -> Int? in
143-
return #^RETURN_TR1_CLOSURE^#
126+
return #^RETURN_TR1_CLOSURE?check=RETURN_TR1^#
144127
}
145128
_ = { () -> Int? in
146-
return g.#^RETURN_TR2_CLOSURE^#
129+
return g.#^RETURN_TR2_CLOSURE?check=RETURN_TR2^#
147130
}
148131
_ = { () -> Int? in
149-
return g.IG.#^RETURN_TR3_CLOSURE^#
132+
return g.IG.#^RETURN_TR3_CLOSURE?check=RETURN_TR3^#
133+
}
134+
}
135+
136+
// Make sure we can do a completion in an out-of-place return
137+
do {
138+
return TestStruct.#^COMPLETE_IN_INVALID_RETURN^#
139+
// COMPLETE_IN_INVALID_RETURN: Decl[StaticMethod]/CurrNominal: testTR1_static()[#Int?#]; name=testTR1_static()
140+
// COMPLETE_IN_INVALID_RETURN: Decl[StaticMethod]/CurrNominal: testTR2_static({#(g): Gen#})[#Int?#]; name=testTR2_static(:)
141+
// COMPLETE_IN_INVALID_RETURN: Decl[StaticMethod]/CurrNominal: testTR3_static({#(g): Gen#})[#Int?#]; name=testTR3_static(:)
142+
}
143+
144+
struct TestReturnInInit {
145+
init() {
146+
return TestStruct.#^COMPLETE_IN_INVALID_INIT_RETURN?check=COMPLETE_IN_INVALID_RETURN^#
150147
}
151148
}

test/stmt/statements.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ return 42 // expected-error {{return invalid outside of a func}}
179179

180180
return // expected-error {{return invalid outside of a func}}
181181

182+
return VoidReturn1() // expected-error {{return invalid outside of a func}}
183+
182184
func NonVoidReturn1() -> Int {
183185
_ = 0
184186
return // expected-error {{non-void function should return a value}}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::getClosureType(swift::ClosureExpr const*) const","signatureAssert":"Assertion failed: (result), function getClosureType"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
return {
44
lazy var a = if .random() { return }
55
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::getTypeOfReferencePre(swift::constraints::OverloadChoice, swift::DeclContext*, swift::constraints::ConstraintLocatorBuilder, swift::constraints::PreparedOverloadBuilder*)","signatureAssert":"Assertion failed: (func->isOperator() && \"Lookup should only find operators\"), function getTypeOfReferencePre"}
2+
// RUN: not %target-swift-frontend -typecheck %s
3+
{
4+
switch if .random() else {
5+
}
6+
{
7+
case let !a where a:
8+
}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// {"kind":"typecheck","signature":"swift::constraints::TypeVarRefCollector::walkToStmtPre(swift::Stmt*)","signatureAssert":"Assertion failed: (result), function getClosureType"}
2+
// RUN: not %target-swift-frontend -typecheck %s
3+
{
4+
guard let a = (a if{
5+
return
6+
}
7+
) "
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// {"kind":"typecheck","signature":"swift::constraints::TypeVarRefCollector::walkToStmtPre(swift::Stmt*)","signatureAssert":"Assertion failed: (result), function getClosureType"}
2+
// RUN: not %target-swift-frontend -typecheck %s
3+
{
4+
switch if <#expression#> {
5+
return
6+
}
7+
{
8+
case let !a where a:
9+
}
10+
}

0 commit comments

Comments
 (0)