Clean up and speed up inference variable resolving code - #160913
Clean up and speed up inference variable resolving code#160913jdonszelmann wants to merge 8 commits into
Conversation
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Clean up and speed up resolving code
This comment has been minimized.
This comment has been minimized.
3dde145 to
b5fe24d
Compare
This comment has been minimized.
This comment has been minimized.
| match self.inner.borrow_mut().type_variables().probe(vid) { | ||
| TypeVariableValue::Known { value } => Ok(value), | ||
| match value { | ||
| TypeVariableValue::Known { value } => Ok(self.shallow_resolve_non_recursive(value)), |
There was a problem hiding this comment.
this method (and the one below) now also does the recursive resolving shallow_resolve already did. No tests change here.
| // Cold because the case in which a tyvar resolves to an intvar which resolves to a type is | ||
| // quite rare. It's way more common for `shallow_resolve_non_recursive` to return ty. | ||
| #[cold] | ||
| fn shallow_resolve_infer_non_recursive(&self, infer: InferTy, ty: Ty<'tcx>) -> Ty<'tcx> { |
There was a problem hiding this comment.
the non-recursive caase helps ~0.5% on local benchmarks. Not much, but still a bit.
| } | ||
|
|
||
| ty::Infer(ty::IntVar(vid)) => { | ||
| let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid); |
There was a problem hiding this comment.
renames all the opportunistic_* functions with simply shallow_resolve_*. I've done this in many places. I've added a lot of docs to all the resolve methods, which I think makes it super clear that resolving is always an opportunistic process that doesn't necessarily resolve all variables, simply because it can't always.
From the perspective of a new contributor, they'll see a resolve_* function for the first time, go to its docs, and learn that the purpose of all resolve_* methods is to opportunistically resolve variables.
Since the behavior is the same for all the resolve_* functions I think that will actually make things clearer than randomly calling some of them "opportunistic" even when the others are inherently also opportunistic.
| /// In cases where we do, this can aid performance. | ||
| #[inline(always)] | ||
| fn shallow_resolve_ty_var(&self, v: TyVid, ty: Ty<'tcx>) -> Ty<'tcx> { | ||
| fn shallow_resolve_ty_var_with_ty(&self, v: TyVid, ty: Option<Ty<'tcx>>) -> Ty<'tcx> { |
There was a problem hiding this comment.
By taking an Option here, we can merge more methods' implementations. Doing this has 0 performance overhead, #[inline(always)] makes sure the callsites that always call with Some get optimized properly.
There was a problem hiding this comment.
could we instead change this function to not take a ty and return and Option instead?
This comment has been minimized.
This comment has been minimized.
|
💔 Test for 7e8d4ec failed: CI. Failed job:
|
b5fe24d to
6b88c26
Compare
|
@bors try (failed due to missing rustdoc changes now applied) |
|
Unknown argument "(failed". Did you mean to use |
This comment has been minimized.
This comment has been minimized.
|
@bors try |
This comment has been minimized.
This comment has been minimized.
Clean up and speed up resolving code
6b88c26 to
06dd748
Compare
This comment has been minimized.
This comment has been minimized.
|
@rustbot review |
| Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid)) | ||
| } | ||
| } | ||
| pub fn shallow_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { |
There was a problem hiding this comment.
if we return None if the inner function doesn't make progress, could we always be explicit about reconstructing the type in the caller to make it explicit where we (likely unnecessarily) do so?
| .borrow_mut() | ||
| .unwrap_region_constraints() | ||
| .opportunistic_resolve_var(canonicalizer.tcx, vid); | ||
| .shallow_resolve_region_var(canonicalizer.tcx, vid); |
There was a problem hiding this comment.
we don't reuse r here if the root doesn't change 🤔 feels like doing so would be good for perf 🤔
|
so we have the following now
|
This comment has been minimized.
This comment has been minimized.
06dd748 to
9ddd601
Compare
This comment has been minimized.
This comment has been minimized.
9ddd601 to
68fded2
Compare
|
The functions are now called:
|
This comment has been minimized.
This comment has been minimized.
…er a shallow_resolve
68fded2 to
aad9f36
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
| .filter(|t| { | ||
| !self.deeply_resolve_ignoring_regions_with_obligations(*t).is_ty_var() | ||
| }) |
There was a problem hiding this comment.
for separate PR: this call to deeply_resolve_ignoring_regions_with_obligations is redundant
| .only_has_type(self) | ||
| .and_then(|ty| self.resolve_vars_with_obligations(ty).opt_tuple_fields()) | ||
| .and_then(|ty| { | ||
| self.deeply_resolve_ignoring_regions_with_obligations(ty).opt_tuple_fields() |
There was a problem hiding this comment.
for separate PR: this call to deeply_resolve_ignoring_regions_with_obligations is redundant
| return Err(TypeError::Mismatch); | ||
| } | ||
| Ok(self.resolve_vars_if_possible(adt_ty)) | ||
| Ok(self.deeply_resolve_ignoring_regions(adt_ty)) |
There was a problem hiding this comment.
for separate PR: this call to deeply_resolve_ignoring_regions_with_obligations is redundant
| let mut pat_ref_layers = 0; | ||
| while let ty::Ref(_, inner_ty, mutbl) = | ||
| *self.resolve_vars_with_obligations(peeled_ty).kind() | ||
| *self.deeply_resolve_ignoring_regions_with_obligations(peeled_ty).kind() |
There was a problem hiding this comment.
for separate PR: this call is redundant
| // region vids before processing regions, so we have a better chance to match clauses | ||
| // in our param-env. | ||
| let (sup_type, sub_region) = eager_resolve_vars(self, (sup_type, sub_region)); | ||
| let (sup_type, sub_region) = deeply_resolve(self, (sup_type, sub_region)); |
There was a problem hiding this comment.
can you make this a method on the InferCtxt 🤔 we should push people to use that instead of deeply_resolve_ignoring_regions :>
| &mut self, | ||
| tcx: TyCtxt<'tcx>, | ||
| vid: ty::RegionVid, | ||
| ) -> ty::Region<'tcx> { |
There was a problem hiding this comment.
also separate: I feel like shallow_resolve_X should return Option and None if the vid doesn't change 🤔
there are a bunch of callers which shouldn't rebuild the ty/region here
| Err(_) => Ty::new_var(self.tcx, self.root_var(vid)), | ||
| } | ||
| fn shallow_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> { | ||
| self.shallow_resolve_ty_var_with_ty(vid, None) |
There was a problem hiding this comment.
I dislike this function. Imo we should instead have shallow_resolve_ty_var_with_ty return Option<Ty> and then do the unwrap_or(ty) in the caller
| /// Resolve a type variable to a type, if known. | ||
| /// Otherwise return a type with the root vid in it. | ||
| #[inline(always)] | ||
| fn shallow_resolve_ty_var_with_ty(&self, v: TyVid, ty: Option<Ty<'tcx>>) -> Ty<'tcx> { |
| } | ||
|
|
||
| impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { | ||
| impl<'a, 'tcx> DeepResolverIgnoringRegions<'a, 'tcx> { |
There was a problem hiding this comment.
maybe just actually call that struct DeeplyResolveIgnoringRegions. DeepResolver sounds odd
|
there are renames and behavior changes here. And I'd like to either first do behavior changes and then renames, or the other way round. but having something that has merge conflicts because it touches a bunch of files while also having subtle changes to the core of infer var handling is :/ |
View all comments
r? @lcnr
Clean up the
resolve*family of functions inInferCtxt, renaming various functions to have more consistent and descriptive names. Adds a lot of documentation, and even wins some performance using the fact that shallow_resolve now returns root vids.Reviewable commit by commit: some are large renames across the board, which are separated from the perf wins
and small renames in other commits to hopefully make more sense.
The functions are now called:
shallow_resolve_*shallow_resolve_*resolve_vars_if_possible_*deeply_resolve_ignoring_regions_*deeply_resolveinstead.eager_resolve_varsdeeply_resolvefully_resolvefully_resolveopportunistic_resolve_varshalllow_resolvenow that we resolve to rootsNote
I've not used an LLM for any part of this PR, or any other PR I make. This includes any related work like research.