Skip to content

Commit 4fc5009

Browse files
committed
[CS] Invalidate VarDecls in targets that fail to type-check
This avoids a crash that could occur when attempting to query their interface type later, which could cause us to attempt to type-check the Decl separately from its enclosing closure. Eventually we also ought to use this to fill in ErrorTypes for expressions (since type-checking ought to only produce typed AST), but I'm leaving that as future work for now. rdar://120012473
1 parent ab4731c commit 4fc5009

File tree

7 files changed

+110
-4
lines changed

7 files changed

+110
-4
lines changed

include/swift/Sema/SyntacticElementTarget.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,10 @@ class SyntacticElementTarget {
887887
llvm_unreachable("invalid target type");
888888
}
889889

890+
/// Marks the target as invalid, filling in ErrorTypes for the AST it
891+
/// represents.
892+
void markInvalid() const;
893+
890894
/// Walk the contents of the application target.
891895
std::optional<SyntacticElementTarget> walk(ASTWalker &walker) const;
892896
};

lib/Sema/SyntacticElementTarget.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,30 @@ bool SyntacticElementTarget::contextualTypeIsOnlyAHint() const {
301301
llvm_unreachable("invalid contextual type");
302302
}
303303

304+
void SyntacticElementTarget::markInvalid() const {
305+
class InvalidationWalker : public ASTWalker {
306+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
307+
// TODO: We ought to fill in ErrorTypes for expressions here; ultimately
308+
// type-checking should always produce typed AST.
309+
return Action::Continue(E);
310+
}
311+
312+
PreWalkAction walkToDeclPre(Decl *D) override {
313+
// Mark any VarDecls and PatternBindingDecls as invalid.
314+
if (auto *VD = dyn_cast<VarDecl>(D)) {
315+
// Only set invalid if we don't already have an interface type computed.
316+
if (!VD->hasInterfaceType())
317+
D->setInvalid();
318+
} else if (isa<PatternBindingDecl>(D)) {
319+
D->setInvalid();
320+
}
321+
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
322+
}
323+
};
324+
InvalidationWalker walker;
325+
walk(walker);
326+
}
327+
304328
std::optional<SyntacticElementTarget>
305329
SyntacticElementTarget::walk(ASTWalker &walker) const {
306330
SyntacticElementTarget result = *this;

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,13 @@ TypeChecker::typeCheckExpression(SyntacticElementTarget &target,
466466
std::optional<SyntacticElementTarget>
467467
TypeChecker::typeCheckTarget(SyntacticElementTarget &target,
468468
TypeCheckExprOptions options) {
469+
auto errorResult = [&]() -> std::optional<SyntacticElementTarget> {
470+
// Fill in ErrorTypes for the target if we can.
471+
if (!options.contains(TypeCheckExprFlags::AvoidInvalidatingAST))
472+
target.markInvalid();
473+
474+
return std::nullopt;
475+
};
469476
DeclContext *dc = target.getDeclContext();
470477
auto &Context = dc->getASTContext();
471478
PrettyStackTraceLocation stackTrace(Context, "type-checking-target",
@@ -475,11 +482,12 @@ TypeChecker::typeCheckTarget(SyntacticElementTarget &target,
475482
// expression and folding sequence expressions.
476483
if (ConstraintSystem::preCheckTarget(
477484
target, /*replaceInvalidRefsWithErrors=*/true)) {
478-
return std::nullopt;
485+
return errorResult();
479486
}
480487

481488
// Check whether given target has a code completion token which requires
482-
// special handling.
489+
// special handling. Returns true if handled, in which case we've already
490+
// type-checked it for completion, and don't need the solution applied.
483491
if (Context.CompletionCallback &&
484492
typeCheckForCodeCompletion(target, /*needsPrecheck*/ false,
485493
[&](const constraints::Solution &S) {
@@ -517,7 +525,7 @@ TypeChecker::typeCheckTarget(SyntacticElementTarget &target,
517525
// Attempt to solve the constraint system.
518526
auto viable = cs.solve(target, allowFreeTypeVariables);
519527
if (!viable)
520-
return std::nullopt;
528+
return errorResult();
521529

522530
// Apply this solution to the constraint system.
523531
// FIXME: This shouldn't be necessary.
@@ -528,7 +536,7 @@ TypeChecker::typeCheckTarget(SyntacticElementTarget &target,
528536
auto resultTarget = cs.applySolution(solution, target);
529537
if (!resultTarget) {
530538
// Failure already diagnosed, above, as part of applying the solution.
531-
return std::nullopt;
539+
return errorResult();
532540
}
533541

534542
// Unless the client has disabled them, perform syntactic checks on the
@@ -571,6 +579,13 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue,
571579
DiagnosticTransaction diagnostics(ctx.Diags);
572580

573581
TypeCheckExprOptions options;
582+
583+
// Avoid invalidating the AST since we'll fall through and try to type-check
584+
// with an archetype contextual type opened. Note we also don't need to call
585+
// `markInvalid` for the target below since we'll replace the expression
586+
// with an ErrorExpr on failure anyway.
587+
options |= TypeCheckExprFlags::AvoidInvalidatingAST;
588+
574589
// Expand macro expansion expression at caller side only
575590
if (!atCallerSide && isa<MacroExpansionExpr>(defaultValue)) {
576591
options |= TypeCheckExprFlags::DisableMacroExpansions;

lib/Sema/TypeChecker.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ enum class TypeCheckExprFlags {
136136

137137
/// Don't expand macros.
138138
DisableMacroExpansions = 0x08,
139+
140+
/// If set, typeCheckExpression will avoid invalidating the AST if
141+
/// type-checking fails. Do not add new uses of this.
142+
AvoidInvalidatingAST = 0x10,
139143
};
140144

141145
using TypeCheckExprOptions = OptionSet<TypeCheckExprFlags>;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// NOTE: This intentionally doesn't use %target-typecheck-verify, this should
2+
// compile without any errors since we're testing the ASTVerifier is happy.
3+
// RUN: %target-swift-frontend -typecheck %s
4+
5+
// Make sure we don't prematurely mark 'x' as invalid.
6+
func testInferenceFromClosureVar<T>(x: T = { var x: Int = 0; return x }()) {}

test/Constraints/type_inference_from_default_exprs.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,6 @@ do {
266266
// expected-warning@-1 {{dictionary literal of type '[String : Int]' has duplicate entries for string literal key 'a'}}
267267
// expected-note@-2 2 {{duplicate key declared here}}
268268
}
269+
270+
func testInferenceFromClosureVarInvalid<T>(x: T = { let x = "" as Int; return x }()) {}
271+
// expected-error@-1 {{cannot convert value of type 'String' to type 'Int' in coercion}}

test/Index/rdar120012473.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: not %target-swift-frontend -typecheck %s -index-store-path %t
3+
4+
let k = ""
5+
6+
// Make sure we don't crash here (rdar://120012473).
7+
func foo() {
8+
_ = {
9+
let x = switch 1 {
10+
case k:
11+
return false
12+
}
13+
return true
14+
}
15+
16+
for x in [0] where ({
17+
let x = switch 1 {
18+
case k:
19+
return false
20+
}
21+
return true
22+
}()) {}
23+
}
24+
25+
@resultBuilder
26+
struct Builder {
27+
static func buildBlock<T>(_ components: T...) -> T {
28+
fatalError()
29+
}
30+
}
31+
32+
@Builder
33+
func bar() -> Bool {
34+
let fn = {
35+
let x = switch 1 {
36+
case k:
37+
return false
38+
}
39+
return true
40+
}
41+
fn()
42+
}
43+
44+
func baz(x: () -> Bool = {
45+
let x = switch 1 {
46+
case k:
47+
return false
48+
}
49+
return true
50+
}) {}

0 commit comments

Comments
 (0)