Skip to content

Commit 0cb1b4a

Browse files
authored
Merge pull request #71930 from hamishknight/to-err
[CS] Invalidate VarDecls in targets that fail to type-check
2 parents f7b8610 + 4fc5009 commit 0cb1b4a

File tree

7 files changed

+168
-5
lines changed

7 files changed

+168
-5
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: 82 additions & 1 deletion
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;
@@ -372,7 +396,64 @@ SyntacticElementTarget::walk(ASTWalker &walker) const {
372396
break;
373397
}
374398
case Kind::forEachStmt: {
375-
if (auto *newStmt = getAsForEachStmt()->walk(walker)) {
399+
// We need to skip the where clause if requested, and we currently do not
400+
// type-check a for loop's BraceStmt as part of the SyntacticElementTarget,
401+
// so we need to skip it here.
402+
// TODO: We ought to be able to fold BraceStmt checking into the constraint
403+
// system eventually.
404+
class ForEachWalker : public ASTWalker {
405+
ASTWalker &Walker;
406+
SyntacticElementTarget Target;
407+
ForEachStmt *ForStmt;
408+
409+
public:
410+
ForEachWalker(ASTWalker &walker, SyntacticElementTarget target)
411+
: Walker(walker), Target(target), ForStmt(target.getAsForEachStmt()) {}
412+
413+
PreWalkAction walkToDeclPre(Decl *D) override {
414+
if (D->walk(Walker))
415+
return Action::Stop();
416+
return Action::SkipNode();
417+
}
418+
419+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
420+
// Ignore where clause if needed.
421+
if (Target.ignoreForEachWhereClause() && E == ForStmt->getWhere())
422+
return Action::SkipNode(E);
423+
424+
E = E->walk(Walker);
425+
426+
if (!E)
427+
return Action::Stop();
428+
return Action::SkipNode(E);
429+
}
430+
431+
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
432+
// We only want to visit the children of the ForEachStmt.
433+
if (S == ForStmt)
434+
return Action::Continue(S);
435+
436+
// But not its body.
437+
if (S != ForStmt->getBody())
438+
S = S->walk(Walker);
439+
440+
if (!S)
441+
return Action::Stop();
442+
443+
return Action::SkipNode(S);
444+
}
445+
446+
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
447+
P = P->walk(Walker);
448+
if (!P)
449+
return Action::Stop();
450+
return Action::SkipNode(P);
451+
}
452+
};
453+
454+
ForEachWalker forEachWalker(walker, *this);
455+
456+
if (auto *newStmt = getAsForEachStmt()->walk(forEachWalker)) {
376457
result.forEachStmt.stmt = cast<ForEachStmt>(newStmt);
377458
} else {
378459
return std::nullopt;

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)