-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[TypeChecker] Don’t crash if a ExplicitCastExpr doesn’t have a cast type #59664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@swift-ci Please smoke test |
Should we add a regression test for that? |
I don’t have a reproducing test case unfortunately. The fix is just based on a crash trace. |
So the radar just included the trace and no code? Hopefully Pavel can come up with one, not too familiar with result builders myself. It could be that the absence of a type here is not benign to begin with. |
I am out this week so I will take a look at this as soon as possible when I am back! |
@@ -3229,7 +3229,7 @@ class ExprAvailabilityWalker : public ASTWalker { | |||
maybeDiagParameterizedExistentialErasure(EE, Where); | |||
} | |||
if (auto *CC = dyn_cast<ExplicitCastExpr>(E)) { | |||
if (!isa<CoerceExpr>(CC) && | |||
if (!isa<CoerceExpr>(CC) && CC->getCastType() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a quick look at this and I might have an idea about what is going on. Based on the stacktrace -diagnoseStmtAvailability
walks into an AbstractClosureExpr
because ExprAvailabilityWalker
always returns true
from shouldWalkIntoClosure
but:
- Result builders currently have multi-statement closure inference disabled due to some ordering issues related to one-way constraints;
- In code completion mode we always pass
LeaveClosureBodiesUnchecked
to the constraint system;
I wonder if something like this might reproduce the crash:
import SwiftUI
struct S<T> {
}
extension S : ExpressibleByIntegerLiteral {
typealias IntegerLiteralType = Int8
init(integerLiteral: IntegerLiteralType) {
}
}
struct MyView : View {
var body: some View {
Group {
EmptyView()
test {
let x = #^COMPLETE^# as S<String>
print(x)
}
}
}
func test(_: () -> Void) -> some View {
return EmptyView()
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That example doesn’t reproduce the crash for me. AFAICT closures aren’t walked
https://github.com/apple/swift/blob/ba42be5fcf2d663a589a3f5e4efe0ffcffb101fa/lib/Sema/MiscDiagnostics.cpp#L64-L65
I very much suspect that this issue will be resolved fundamentally when we get rid of LeaveClosureBodiesUnchecked
and I’m wondering if it makes sense to hunt for a reproducing test case now since the fix seems pretty straight-forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check you pointed out would skip walking into some decls in the closures, not the closures themselves though. I think this issue needs to be addressed in a different way by adjusting the check in ExprAvailabilityWalker
, otherwise it would just crash in some other place…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I managed to find a reproducer that’s not code completion specific. With this reproducer at hand, I would argue that the fix is correct because AFAICT it appears correct that a ExplicitCastExpr
has no CastType
if that type is invalid. So we need to check if the type is valid (i.e. not null) before calling hasParameterizedExistential
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did an expression with incorrect cast end up getting past to a syntactic diagnostics?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAICT it appears correct that a
ExplicitCastExpr
has noCastType
if that type is invalid.
I don't know for sure if that makes sense in this case, but some other places I saw where the type was invalid the expected was to have an ErrorType
instead of a null type. But again not sure if that is the case here...
… type If the type of an `ExplicitCastExpr` is not valid, it is a null type, which causes a crash when checking if the cast type has parameterized existentials. rdar://95629905
5b7712c
to
28a8e07
Compare
ExplicitCastExpr
doesn’t have a cast type28a8e07
to
3f82b4c
Compare
@swift-ci Please smoke test |
@swift-ci Please SourceKit stress test |
include/swift/AST/Decl.h
Outdated
struct TypecheckedBodyResult { | ||
/// The body of the function with as many statements type checked as | ||
/// possible. | ||
BraceStmt *TypecheckedBody; | ||
/// If a single statement in the type checked body has an error, this is | ||
/// \c true. | ||
bool HadError; | ||
}; | ||
|
||
/// Retrieve the type-checked body of the given function, or \c nullptr if | ||
/// there's no body available. | ||
BraceStmt *getTypecheckedBody() const; | ||
TypecheckedBodyResult getTypecheckedBody() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why this change is needed, is it not sufficient to return an ErrorExpr
body if we encounter an error? For some context, we eventually want SILGen to be able to handle emitting potentially invalid ASTs with an inverted compiler pipeline, so the idea was we could teach SILGen to emit an ErrorExpr
as an undef
and just emit bodies that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because editor functionality (most notably cursor info + indexing) still wants to walk the partially type-checked brace statement to salvage as much information as possible from it. If we just return ErrorExpr
(or similar here), that’s no longer possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm okay, makes sense. Eventually we really ought to do this at a finer grained level though, e.g have typeCheckExpression
produce individual ErrorExpr
s or type expressions as ErrorType
. It's really unfortunate to have type-checking requests return un-type-checked AST on failure.
3f82b4c
to
28a8e07
Compare
The stress tester found issues with the principled fix of propagating I decided to scope the fix down to the original |
@swift-ci Please smoke test |
If the type of an ExplicitCastExpr is not valid, it is a null type, which causes a crash when checking if the cast type has parameterized existentials.
rdar://95629905