-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Fix drop tracking ICEs and re-enable generator drop tracking #93180
Conversation
I'd like to have PR artifacts so that people can check whether this fixes their ICEs @bors try
There are ICEs being reported on nightly, so it would probably be safer to:
|
⌛ Trying commit 197640adea324c9fdedd3162286fdbb84f50805a with merge 12c8eb434c751050bfbc9b7e270af75a2ef98b4c... |
☀️ Try build successful - checks-actions |
@eholk this may fix issue #93161 but doesn't seem to fix some of the other ICEs (thanks to @Eijebong for the help). You can try to build Locally (with
|
I agree that this is the right course of action. I will continue working on this PR to fix any known PRs and make sure to do a crater run before merging it. |
197640a
to
e1bf8bc
Compare
@@ -749,9 +749,15 @@ fn sanitize_witness<'tcx>( | |||
} | |||
let decl_ty = tcx.normalize_erasing_regions(param_env, decl.ty); | |||
|
|||
let is_uninhabited = tcx.is_ty_uninhabited_from( |
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.
let is_uninhabited = tcx.is_ty_uninhabited_from( | |
let is_inhabited = !tcx.is_ty_uninhabited_from( |
// Sanity check that typeck knows about the type of locals which are | ||
// live across a suspension point | ||
if !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) { | ||
if !is_uninhabited && !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) { |
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.
if !is_uninhabited && !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) { | |
if is_inhabited && !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) { |
This if condition is sufficiently complex that, to me at least, it is easier to read this way.
Alternatively, I would consider:
if is_uninhabited || allowed.contains(&decl_ty) || allowed_upvars.contains(&decl_ty) {
// This type which appears in the generator either...
// - is uninhabited, in which case it can't actually be captured at runtime
// - appears in the approximation from thje static type (`allowed`)
// - appears in the lsit of upvars ...
} else {
span_bug!(...);
}
(only those comments are wrong)
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 said, maybe we should move the uninhabited check somewhere else so that we just don't capture the type at the MIR level?
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 looked at that a little at first and gave up. The right place to do this is probably in either locals_live_across_suspend_points
or maybe in the MaybeLiveLocals
MIR pass that locals_live_across_suspend_points
uses. In MaybeLiveLocals
, I didn't see a great way to get access to the tcx
to ask if a type is uninhabited, but I wouldn't be surprised if there's an obvious way I don't know about.
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 like your rephrasing of the conditional, so I appliked your suggestion.
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.
#93313 exposes information about uninhabited types in call expressions to analyses running on MIR, and would be an another way to close the precision gap.
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.
@tmiasko - do you know if you change fixes this same issue as well? If so, I think yours is the more principled fix.
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.
It does fix the test case included here.
I understood the difference to be caused by modelling of calls with uninhabited calls as not returning in handle_uninhabited_return
, so #93313 should provide a MIR equivalent.
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.
Awesome, thanks! I'll leave my version here for now, but if yours merges before this one I'll pull it out and rely on yours since it's better.
I've updated the PR to fix the known ICEs introduced by drop tracking. This means the PR now does three separate things (description is updated to match). Would folks prefer I split it into three PRs? It would be good to do a crater run before merging this PR (or at least part 3) to uncover any other ICEs we may not have discovered yet. |
Are the three separate things reflected as individual commits? From my reading of your description and of the commit series, that seems to be the case, and I think we are fine with having them being collected in one PR here in that case. (If for some reason we want to backport one commit and not another, we can wrestle with that then.)
Sounds like a good idea. |
@bors try |
⌛ Trying commit 487ba6534c7d4259446d3861977fbbb053153e22 with merge 2cd6942e35a7765b6869d3a149ca54f2598b77ad... |
It's not exactly one commit per thing, but I don't think there's any overlap between them. I can rebase into three commits if that's helpful. |
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@craterbot run mode=build-only |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
/// but this analysis only looks at expressions. In case a break has a block as a | ||
/// target, this will find the last expression in the block and return its HirId | ||
/// instead. | ||
fn find_target_expression(&self, hir_id: HirId) -> HirId { |
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.
Comment/name could be improved: the fn seems to only find the target for breaks, but the name sounds generic, and the comment mentions both break and continue. Also, what is hir_id
, is it the hir-id of the break? etc.
@@ -334,7 +373,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { | |||
} | |||
ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..) | |||
| ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => { | |||
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target); | |||
self.drop_ranges | |||
.add_control_edge_hir_id(self.expr_index, self.find_target_expression(target)); |
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.
oh, I guess it is used for both break/continue-- that confuses me a bit. It would be good to give an example
'a: {
...
break 'a
}
'a: loop {
...
continue 'a
}
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.
It's probably confusing because it's wrong. Here's an example that fails:
let mut arr = [Ptr];
let mut count = 0;
drop(arr);
while count < 3 {
count += 1;
yield;
if b {
arr = [Ptr];
continue;
}
}
I mistakenly assumed that Break
and Continue
worked like goto
where the Destination
field pointed at the expression where control flow transferred to. It actually points to the block being broken or continued. It mostly worked because of the way post-order numbering worked.
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 just uploaded a new commit that handles continue correctly.
@eholk r=me with function comment improved :) |
@rustbot ready |
@rustbot author |
Thanks for the feedback! It turns out I wasn't handling The crater report isn't finished, but it looks like there were 739 ICEs. I think we should wait to see the report before merging this. Hopefully they were all due to the issue with continue. |
☔ The latest upstream changes (presumably #93284) made this pull request unmergeable. Please resolve the merge conflicts. |
Drop tracking in the generator interior type checking pass would count all values in unreachable code as dropped (e.g. code after a call to a function with an uninhabited return type), which would lead to those values not being included in the witness type. This resulted in the type checker being more precise than the corresponding sanity check in the MIR transform. This patch changes the check in the MIR code to match the results of typeck by skipping uninhabited types.
1109fa3
to
e5b2270
Compare
@craterbot retry-report |
🛠️ Generation of the report for ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
As we talked about in the async triage meeting today, I don't think it makes sense to do this as a mega-PR that fixes all the ICEs we can find. Instead I'd like to break out the individual fixes and then do a PR that just re-enables drop tracking. I propose instead of the patches from this one that deal with the For the break and continue handling, I've pulled these patches into #93752. I'm going to start spot-testing these patches against different crates that failed the crater run and see if those look we can kick off another one that retries just the failed crates. |
Generator drop tracking recently was merged (#91032) but unfortunately caused several ICEs. The code is currently disabled (#93165, #93284). This PR fixes the underlying issues and re-enables drop range tracking.
There are several changes involved:
break
andcontinue
expressionsAllow uninhabited types in generator interiors
Drop tracking in the generator interior type checking pass would count all values in unreachable code as dropped (e.g. code after a call to a function with an uninhabited return type), which would lead to those values not being included in the witness type. This resulted in the type checker being more precise than the corresponding sanity check in the MIR transform.
This patch changes the check in the MIR code to match the results of typeck by skipping uninhabited types.
Fixes #93161
Support block-target
break
andcontinue
expressionsThe target of a
break
orcontinue
expression is usually an expression, but can also be a block. This situation arises, for example, intry
blocks. Drop tracking did not previously handle this case. To fix it, we look up the target of the expression and if it's a block we use the hir_id of the tail expression of the block rather than the block itself. This is because the drop tracking analysis only gives PostOrderIds to expressions, but not to blocks.An alternative would be to give blocks a PostOrderId as well, but this needs to be kept in sync with the scope analysis in region.rs, so it seemed better not to have another pair of code blocks that need to be kept in sync.
Re-enable drop tracking
Changes the
ENABLE_DROP_TRACKING
flag totrue
ingenerator_interior.rs
and re-enables all the tests that are enabled by it.r? @nikomatsakis