Use closures more consistently in `dep_graph.rs`.
This file has several methods that take a `FnOnce() -> R` closure:
- `DepGraph::with_ignore`
- `DepGraph::with_query_deserialization`
- `DepGraph::with_anon_task`
- `DepGraphData::with_anon_task_inner`
It also has two methods that take a faux closure via an `A` argument and a `fn(TyCtxt<'tcx>, A) -> R` argument:
- DepGraph::with_task
- DepGraphData::with_task
The rationale is that the faux closure exercises tight control over what state they have access to. This seems silly when (a) they are passed a `TyCtxt`, and (b) when similar nearby functions take real closures. And they are more awkward to use, e.g. requiring multiple arguments to be gathered into a tuple. This commit changes the faux closures to real closures.
r? @Zalathar
From `plumbing.rs` to `execution.rs`, which is where most of the other
query profiling occurs. It also avoids eliminates some fn parameters.
This means the `provided_to_erased` call in `try_from_load_disk_fn` is
now included in the profiling when previously it wasn't. This is
good because `provided_to_erased` is included in other profiling calls
(e.g. calls to `invoke_provider_fn`).
It doesn't need to be in there, it can instead be at the single call
site. Removing it eliminates one parameter, makes `define_queries!`
smaller (which is always good), and also enables the next commit which
tidies up profiling.
This commit also changes how `value` and `verify` are initialized,
because I don't like the current way of doing it after the declaration.
This file has several methods that take a `FnOnce() -> R` closure:
- `DepGraph::with_ignore`
- `DepGraph::with_query_deserialization`
- `DepGraph::with_anon_task`
- `DepGraphData::with_anon_task_inner`
It also has two methods that take a faux closure via an `A` argument and
a `fn(TyCtxt<'tcx>, A) -> R` argument:
- DepGraph::with_task
- DepGraphData::with_task
The rationale is that the faux closure exercises tight control over what
state they have access to. This seems silly when (a) they are passed a
`TyCtxt`, and (b) when similar nearby functions take real closures. And
they are more awkward to use, e.g. requiring multiple arguments to be
gathered into a tuple. This commit changes the faux closures to real
closures.
Clean up query-forcing functions
This PR takes the `force_query` function, inlines it into its only caller `force_from_dep_node_inner`, and renames the resulting function to `force_query_dep_node`. Combining the functions became possible after the removal of `rustc_query_system`, because they are now in the same crate.
There are two other notable cleanups along the way:
- Removing an unhelpful assertion and its verbose comment
- The removed comment was originally added in https://github.com/rust-lang/rust/commit/0454a41, but is too verbose to be worth preserving inline.
- Removing a redundant cache lookup while forcing, as it is useless in the serial compiler and unnecessary (and probably unhelpful) in the parallel compiler
r? nnethercote
Prior to #154122 it wasn't used on all paths, so we only computed it
when necessary -- sometimes in `check_if_ensure_can_skip_execution`,
sometimes in one of two places in `execute_job_incr` -- and pass around
`Option<DepNode>` to allow this.
But now it's always used, so this commit makes us compute it earlier,
which simplifies things.
- `check_if_ensure_can_skip_execution` can be made simpler, returning a
bool and eliminating the need for `EnsureCanSkip`.
- `execute_job_incr` no longer uses two slightly different methods to
create a `DepNode` (`get_or_insert_with` vs `unwrap_or_else`).
The call chain for a non-incremental query includes the following
functions:
- execute_query_non_incr_inner (assert!)
- try_execute_query (assert!)
- execute_job_non_incr (assert!)
And likewise for an incremental query:
- execute_query_incr_inner (assert!)
- try_execute_query (assert!)
- execute_job_incr (expect)
That is five distinct functions. Every one of them has an `assert!` or
`expect` call that checks that the dep-graph is/is not enabled as
expected. Three cheers for defensive programming but this feels like
overkill, particularly when `execute_job{,_non_incr,_incr}` each have a
single call site.
This commit removes the assertions in `execute_query_*` and
`try_execute_query`, leaving a check in each of the `execute_job_*`
functions.
The existing names have bugged me for a while. Changes:
- `CycleError` -> `Cycle`. Because we normally use "error" for a `Diag`
with `Level::Error`, and this type is just a precursor to that. Also,
many existing locals of this type are already named `cycle`.
- `CycleError::cycle` -> `Cycle::frames`. Because it is a sequence of
frames, and we want to avoid `Cycle::cycle` (and `cycle.cycle`).
- `cycle_error` -> `find_and_handle_cycle`. Because that's what it does.
The existing name is just a non-descript noun phrase.
- `mk_cycle` -> `handle_cycle`. Because it doesn't make the cycle; the
cycle is already made. It handles the cycle, which involves (a)
creating the error, and (b) handling the error.
- `report_cycle` -> `create_cycle_error`. Because that's what it does:
creates the cycle error (i.e. the `Diag`).
- `value_from_cycle_error` -> `handle_cycle_error_fn`. Because most
cases don't produce a value, they just emit an error and quit.
And the `_fn` suffix is for consistency with other vtable fns.
- `from_cycle_error` -> `handle_cycle_error`. A similar story for this
module.
The different parts of this function used to be split across different crates,
but nowadays they are both in `rustc_query_impl`, so they can be combined.
This commit also hoists the vtable lookup to a closure in
`make_dep_kind_vtable_for_query`, to reduce the amount of code that deals with
`GetQueryVTable`, and to make the inner function more consistent with other
functions in `execution`.
In the serial compiler, we should never be trying to force a query node that
has a cached value, because we would have already marked the node red or green
when putting its value in cache.
In the parallel compiler, we immediately re-check the cache while holding the
state shard lock anyway, and trying to avoid that lock doesn't seem worthwhile.
By removing the early return and using a `match` instead.
- The two paths are of similar conceptual weight, and `match` reflects
that.
- This lets the `incremental_verify_ich` call be factored out.
We can only reach this point if `try_load_from_disk_fn` fails, and the
condition of this assertion is basically just the inverse of what
`try_load_from_disk_fn` does. Basically it's like this:
```
fn foo() -> bool { a && b }
fn bar() {
if foo() {
return;
}
assert!(!a || !b);
}
```
The assertion is just confusing and provides little value.
Code that previously used `QueryStackFrame` now uses `TaggedQueryKey` directly.
Code that previously used `Spanned<QueryStackFrame>` now uses
`QueryStackFrame`, which includes a span.
An explicit dep-kind is redundant here. If we want to find the query name, or
check for specific queries, we can inspect the `TaggedQueryKey` instead.
Similarly, in cases where the key is some kind of `DefId`, we can extract it
directly from the `TaggedQueryKey`.
Re-export `rustc_middle::query::{QuerySystem, QueryVTable}`
- Follow-up to https://github.com/rust-lang/rust/pull/153760#discussion_r2928848418.
---
All of the other public items in `rustc_middle::query::plumbing` are re-exported from `query`, except for these two, for no particular reason that I can see.
Re-exporting them allows `rustc_middle::query::plumbing` to have its visibility reduced to pub(crate).
Imports within `rustc_middle` have also been updated to consistently use the re-exports in `crate::query`.
r? nnethercote
All of the other public items in `rustc_middle::query::plumbing` are
re-exported from `query`, except for these two, for no particular reason that I
can see.
Re-exporting them allows `rustc_middle::query::plumbing` to have its visibility
reduced to pub(crate).
Imports within `rustc_middle` have also been updated to consistently use the
re-exports in `crate::query`.
There are four functions that use `for_each_query_vtable!` to call an "inner"
function. They are:
- collect_active_jobs_from_all_queries -> gather_active_jobs
- alloc_self_profile_query_strings -> alloc_self_profile_query_strings_for_query_cache
- encode_all_query_results -> encode_query_results
- query_key_hash_verify_all -> query_key_hash_verify
These names are all over the place. This commit renames them as follows:
- collect_active_query_jobs{,_inner}
- alloc_self_profile_query_strings{,_inner}
- encode_query_values{,_inner}
- verify_query_key_hashes{,_inner}
This:
- puts the verb at the start
- uses "inner" for all the inners (which makes sense now that the inners are
all next to their callers)
- uses `_query_` consistently
- avoids `all`, because the plurals are enough
- uses `values` instead of `results`
- removes the `collect`/`gather` distinction, which is no longer
important
Moving the macro and its expansion into the same physical file resolves a lot
of tension in the current module arrangement.
Code in the macro is now free to use plain imports in the same file, and there
is no longer any question of whether `mod query_impl` should be declared inside
the macro, or surrounding a separate expansion site.
Remove `CycleErrorHandling`.
This PR removes the `cycle_*` query modifiers and `CycleErrorHandling`. Some good simplicity wins. Details in individual commits.
r? @Zalathar
Currently if a cycle error occurs the error is created, then handled
according to `CycleErrorHandling` (which comes from the
`cycle_delay_bug` query modifer, or lack thereof), and then
`value_from_cycle_error` is called.
This commit changes things so the error is created and then just passed
to `value_from_cycle_error`. This gives `value_from_cycle_error` full
control over how it's handled, eliminating the need for
`CycleErrorHandling`. This makes things simpler overall.
`collect_active_jobs_from_all_queries` takes a `require_complete` bool,
and then some callers `expect` a full map result while others allow a
partial map result. The end result is four possible combinations, but
only three of them are used/make sense.
This commit introduces `CollectActiveJobsKind`, a three-value enum that
describes the three sensible combinations, and rewrites
`collect_active_jobs_from_all_queries` around it. This makes it and its
call sites much clearer, and removes the weird `Option<()>` and
`Result<QueryJobMap, QueryJobMap>` return types.
Other changes of note.
- `active` is removed. The comment about `make_frame` is out of date,
and `create_deferred_query_stack_frame` *is* safe to call with the
query state locked.
- When shard locking failure is allowed, collection no longer stops on
the first failed shard.
Add a `TaggedQueryKey` to identify a query instance
This adds back a `TaggedQueryKey` enum which represents a query kind and the associated key. This is used to replace `QueryStackDeferred` and `QueryStackFrameExtra` and the associated lifting operation for cycle errors
This approach has a couple of benefits:
- We only run description queries when printing the query stack trace in the panic handler
- The unsafe code for `QueryStackDeferred` is removed
- Cycle handles have access to query keys, which may be handy
Some further work could be replacing `QueryStackFrame` with `TaggedQueryKey` as the extra information can be derived from it.
`execute_job` has a single call site in `try_execute_query`. This commit
inlines and removes `execute_job`, but also puts the part that checks
feedable results in its own separate function, `check_feedable`, because
it's a nicely separate piece of logic.
The big win here is that all the code dealing with the `QueryState` is
now together in `try_execute_query`: get the lock, do the lookup, drop
the lock, create the job guard, and complete the job guard. Previously
these steps were split across two functions which I found hard to
follow.
This commit purely moves code around, there are no other changes.
Introduce `for_each_query_vtable!` to move more code out of query macros
After https://github.com/rust-lang/rust/pull/153114 moved a few for-each-query functions into the big `rustc_query_impl::plumbing` macro, I have found that those functions became much harder to navigate and modify, because they no longer have access to ordinary IDE features in rust-analyzer. Even *finding* the functions is considerably harder, because a plain go-to-definition no longer works smoothly.
This PR therefore tries to move as much of that code back out of the macro as possible, with the aid of a smaller `for_each_query_vtable!` helper macro. A typical use of that macro looks like this:
```rust
for_each_query_vtable!(ALL, tcx, |query| {
query_key_hash_verify(query, tcx);
});
```
The result is an outer function consisting almost entirely of plain Rust code, with all of the usual IDE affordances expected of normal Rust code. Because it uses plain Rust syntax, it can also be formatted automatically by rustfmt.
Adding another layer of macro-defined macros is not something I propose lightly, but in this case I think the improvement is well worth it:
- The outer functions can once again be defined as “normal” Rust functions, right next to their corresponding inner functions, making navigation and modification much easier.
- The closure expression is ordinary Rust code that simply gets repeated ~300 times in the expansion, once for each query, in order to account for the variety of key/value/cache types used by different queries. Even within the closure expression, IDE features still *mostly* work, which is an improvement over the status quo.
- For future maintainers looking at the call site, the macro's effect should hopefully be pretty obvious and intuitive, reducing the need to even look at the helper macro. And the helper macro itself is largely straightforward, with its biggest complication being that it necessarily uses the `$name` metavar from the outer macro.
There should be no change to compiler behaviour.
r? nnethercote (or compiler)
There is a bunch of complexity supporting the "cannot check whether the
hidden type of opaque type satisfies auto traits" error that shows up in
`tests/ui/impl-trait/auto-trait-leak.rs`. This is an obscure error that
shows up in a single test. If we are willing to downgrade that error
message to a cycle error, we can do the following.
- Simplify the `type_of_opaque` return value.
- Remove the `cycle_stash` query modifier.
- Remove the `CyclePlaceholder` type.
- Remove the `SelectionError::OpaqueTypeAutoTraitLeakageUnknown`
variant.
- Remove a `FromCycleError` impl.
- Remove `report_opaque_type_auto_trait_leakage`.
- Remove the `StashKey::Cycle` variant.
- Remove the `CycleErrorHandling::Stash` variant.
That's a lot! I think this is a worthwhile trade-off.
`ActiveJobGuard::complete` and `ActiveJobGuard::drop` have very similar
code, though non-essential structural differences obscure this a little.
This commit factors out the common code into a new method
`ActiveJobGuard::complete_inner`. It also inlines and remove
`expect_job`, which ends up having a single call site.
Replace the `try_mark_green` hook with direct calls to `tcx.dep_graph`
All of the existing call sites are directly touching `tcx.dep_graph` anyway, so the extra layer of indirection provides no real benefit.
There should be no change to compiler behaviour.
r? nnethercote (or compiler)
Currently we use a mix of `C::Key` and `&C::Key` parameters. The former
is more common and a bit nicer, so convert some of the latter. This
results in less converting between the two types, and fewer sigils.
Remove `tls::with_related_context`.
This function gets the current `ImplicitCtxt` and checks that its `tcx` matches the passed-in `tcx`. It's an extra bit of sanity checking: when you already have a `tcx`, and you need access to the non-`tcx` parts of `ImplicitCtxt`, check that your `tcx` matches the one in `ImplicitCtxt`.
However, it's only used in two places: `start_query` and `current_query_job`. The non-checked alternatives (`with_context`, `with_context_opt`) are used in more places, including some where a `tcx` is available. And things would have to go catastrophically wrong for the check to fail -- e.g. if we somehow end up with multiple `TyCtxt`s. In my opinion it's just an extra case to understand in `tls.rs` that adds little value.
This commit removes it. This avoids the need for `tcx` parameters in a couple of places. The commit also adjusts how `start_query` sets up its `ImplicitCtxt` to more closely match how similar functions do it, i.e. with `..icx.clone()` for the unchanged fields.
r? @oli-obk
There are three query vtable functions related to the `cache_on_disk_if`
modifier:
- `will_cache_on_disk_for_key_fn`
- `try_load_from_disk_fn`
- `is_loadable_from_disk_fn`
These are all function ptrs within an `Option`. They each have a wrapper
that returns `false`/`None` if the function ptr is missing.
This commit removes the `Option` wrappers. In the `None` case we now set
the function ptr to a trivial closure that returns `false`/`None`. The
commit also removes some typedefs that each have a single use. All this
is a bit simpler, with less indirection.
Note that `QueryVTable::hash_value_fn` is still an `Option`. Unlike the
above three functions, which have trivial behaviour in the `None` case,
`hash_value_fn` is used in ways where the `Some`/`None` distinction is
more meaningful.
This removes the `cycle_fatal` query modifier as it has no effect on its current users.
The default `CycleErrorHandling::Error` mode does the same as `cycle_fatal` when the default impl of `FromCycleError` is used. The return types of queries using `cycle_fatal` however have no specialized `FromCycleError` impl.
This function gets the current `ImplicitCtxt` and checks that its `tcx`
matches the passed-in `tcx`. It's an extra bit of sanity checking: when
you already have a `tcx`, and you need access to the non-`tcx` parts of
`ImplicitCtxt`, check that your `tcx` matches the one in `ImplicitCtxt`.
However, it's only used in two places: `start_query` and
`current_query_job`. The non-checked alternatives (`with_context`,
`with_context_opt`) are used in more places, including some where a
`tcx` is available. And things would have to go catastrophically wrong
for the check to fail -- e.g. if we somehow end up with multiple
`TyCtxt`s. In my opinion it's just an extra case to understand in
`tls.rs` that adds little value.
This commit removes it. This avoids the need for `tcx` parameters in a
couple of places. The commit also adjusts how `start_query` sets up its
`ImplicitCtxt` to more closely match how similar functions do it, i.e.
with `..icx.clone()` for the unchanged fields.