-
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
Implement tainted_by_errors
in MIR borrowck, use it to skip CTFE
#93691
Conversation
I wonder what other places we could use this tainted field that we generate during MIR borrowck... |
This comment has been minimized.
This comment has been minimized.
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 think cbec6f8 is too invasive and does not fit how most queries handle errors. Maybe we should just add a tainted_by_errors
field to mir::Body
, too?
What do you think?
compiler/rustc_borrowck/src/lib.rs
Outdated
|
||
pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) { | ||
self.tainted_by_errors = true; | ||
t.buffer(&mut self.errors_buffer); |
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.
Seems a bit fragile to keep exposing the buffer list. Maybe nest it in some structure that contains the tainted field and the errors? Or at least leave a FIXME for it ^^
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.
Not sure what you mean by this.
This method on MirBorrowckCtxt is just convenience to both buffer the diagnostic (which we are already doing before this diff) and set tainted_by_errors to true, since I didn't want to have to duplicate setting that boolean (or calling a method to set that boolean) in like the 20 spots I introduced this helper.
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.
Sorry. What I mean is that the previous way is still possible. So people may add invocations of .buffer
on the diagnostic and thus keep forgetting to set the tainted flag. If we hide the buffer as a private field of a struct we can avoid people accessing it directly.
It's more of a concern for careless impls in the future than a problem with the PR.
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.
As an alternative we could check the sites where the buffer is consumed. If it is not empty, taint the borrowck result
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 yeah, good point, I can hide buffer in a harder-to-access field to prevent people accidentally not setting tainted with it too.
I can't just set tainted when we drain the buffer later because DiagnosticBuilder::buffer sometimes just emits the error directly instead of buffering it depending on some settings.
@oli-obk, so I thought about putting it into Body, but it looks like borrowck actually treats Body as immutable in favor of returning a different BorrowCheckResult struct, and I didn't want to do something with interior mutability or reallocating a new Body... |
I was thinking of doing it where you now return We could instead just check borrowck (and probably typeck, too) in |
Not opposed to this necessarily, but even if we do borrowck again in |
Oh yes, we need all your borrowck changes , only now the mir queries don't change ^^ |
oh sure, let me see if i can do that then. |
2afbfaf
to
bc9ea71
Compare
tainted_by_errors
in MIR borrowck, use it to skip CTFEtainted_by_errors
in MIR borrowck, use it to skip CTFE
@oli-obk: Yeah, I didn't need to There are two places I do a borrowck call ( |
This comment has been minimized.
This comment has been minimized.
@@ -833,13 +833,14 @@ rustc_queries! { | |||
/// additional requirements that the closure's creator must verify. | |||
query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { | |||
desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } | |||
cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } | |||
cache_on_disk_if { true } |
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.
Why is this necessary?
I'm going to do a perf run,? but maybe we just need to check the tainted flag here, too
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 was getting ICEs in incremental tests complaining that the MIR body for a fn was stolen, since we're now calling borrowck which depends on unoptimized MIR which is probably gone if we do const eval late in the compilation process...
Not sure if only caching if the body if it is tainted works, since we're unconditionally calling borrowck before const eval.
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.
heh, yea, this is what @BoxyUwU ran into, too, and we never got as far as to figuring it out.
I guess it makes sense that if the cache rules differ between two queries that work on stealing things, that said caching breaks, but it's really opaque to me. Well... let's see what perf says
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.
Perf does show the additional query result serialization, so we should try to do something here.
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 not worth it, but we could try adding a query that returns just an Option<ErrorReported>
and does all the processing of typeck, borrowck and const qualifs. Then we can only cache that on disk and it should rarely get recomputed. But: new query so probably overkill, but worth an experiment?
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.
Yeah, I'm not exactly sure what we can do to limit cache_on_disk
just to when it's needed, since it seems like we need to cache it for any body that has a promoted const, see the failing query stack trace:
query stack during panic:
#0 [mir_borrowck] borrow-checking `lit_test::lit_test`
#1 [eval_to_allocation_raw] const-evaluating + checking `lit_test::lit_test::promoted[1]`
#2 [eval_to_allocation_raw] const-evaluating + checking `lit_test::lit_test::promoted[1]`
#3 [optimized_mir] optimizing MIR for `lit_test::lit_test`
#4 [collect_and_partition_mono_items] collect_and_partition_mono_items
... but we don't know if a body has any promoted consts until after borrowck.
so I might try the query experiment you suggested. I'll ping you for a perf run when I put it up, since I don't have super powers.
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.
Actually, we do know it at borrowck time, so we could encode that info in the borrowck result.
But maybe we're going about this the wrong way. When we force mir borrowck and then steal the mir, we own the mir, so we can mutate it and change its tainted field. This is essentially the same idea as the merging of the tainted flags into one query only we're putting it right in the data we have to load anyway
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.
Actually, we do know it at borrowck time, so we could encode that info in the borrowck result.
Oh lol, I thought we did borrowck on mir_built
, whoopsy.
When we force mir borrowck and then steal the mir, we own the mir, so we can mutate it and change its tainted field.
So you're suggesting we do add the tainted field to mir::Body
then, instead of BorrowCheckerResult
?
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.
You need it on both, as (as you reminded me) you can't mutate the mir from borrowck. But at the query that steals the mir after forcing the borrowck query, you can just call borrowck instead of forcing it and copy the tainted field over to the mir body you just stole
@@ -214,6 +214,7 @@ pub struct BorrowCheckResult<'tcx> { | |||
pub concrete_opaque_types: VecMap<OpaqueTypeKey<'tcx>, Ty<'tcx>>, | |||
pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>, | |||
pub used_mut_upvars: SmallVec<[Field; 8]>, | |||
pub tainted_by_errors: bool, |
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.
Please make it an option like in the typeck results .this will make it easier for the in-flight PRs that make our error handling more solid
@@ -287,6 +287,9 @@ pub fn eval_to_allocation_raw_provider<'tcx>( | |||
if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors { |
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.
A bit more than planned for this PR, but if you want to try doing this cleanup in this PR, I'm not going to complain ;)
Basically remove the entire logic in the is_local
if block and move whatever is missing in load_mir
to it.
We can also guard the checks in load_mir
with the is_local check, as any mir we load from dependencies must be error free
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, feel free not to do this in this PR .we can merge it soon in any case
@bors try @rust-timer queue |
Awaiting bors try build completion. @rustbot label: +S-waiting-on-perf |
⌛ Trying commit eb42e801ea203377536cf31ad51caae5cdcd1449 with merge 86a277ecc46223ab296a8b964b834266a79a9d1f... |
💥 Test timed out |
@bors try @rust-timer queue |
Awaiting bors try build completion. @rustbot label: +S-waiting-on-perf |
⌛ Trying commit eb42e801ea203377536cf31ad51caae5cdcd1449 with merge d106320594d82e721bfc144b18d7f1db27135b68... |
☀️ Try build successful - checks-actions |
72f265e
to
67ad0ff
Compare
rebased, an error message went away but it was definitely due to opaque types PR being reverted. @rustbot ready |
@bors r+ |
📌 Commit 67ad0ff has been approved by |
☀️ Test successful - checks-actions |
Finished benchmarking commit (9cdefd7): comparison url. Summary: This benchmark run shows 5 relevant improvements 🎉 but 3 relevant regressions 😿 to instruction counts.
If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf. Next Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression |
@rustbot label: +perf-regression-triaged This fixes a bunch of compiler crashes that occurred because we weren't checking for borrowck errors before doing const eval. The additional work is entirely expected and other avenues did not pan out. |
Putting this up for initial review. The issue that I found is when we're evaluating a const, we're doing borrowck, but doing nothing with the fact that borrowck fails.
This implements a
tainted_by_errors
field for MIR borrowck like we have in infcx, so we can use that information to return anErr
during const eval if our const fails to borrowck.This PR needs some cleaning up. I should probably just use
Result
in more places, instead of.expect
ing in the places I am, but I just wanted it to compile so I could see if it worked!Fixes #93646
r? @oli-obk
feel free to reassign