Skip to content

Commit e9e5291

Browse files
committed
Auto merge of #135911 - Zalathar:arena-cache-option, r=<try>
Allow `arena_cache` queries to return `Option<&'tcx T>` Currently, `arena_cache` queries always have to return `&'tcx T`[^deref]. This means that if an arena-cached query wants to return an optional value, it has to return `&'tcx Option<T>`, which has a few negative consequences: - It goes against normal Rust style, where `Option<&T>` is preferred over `&Option<T>`. - Callers that actually want an `Option<&T>` have to manually call `.as_ref()` on the query result. - When the query result is `None`, a full-sized `Option<T>` still needs to be stored in the arena. This PR solves that problem by introducing a helper trait `ArenaCached` that is implemented for both `&T` and `Option<&T>`, and takes care of bridging between the provided type, the arena-allocated type, and the declared query return type. --- To demonstrate that this works, I have converted the two existing arena-cached queries that currently return `&Option<T>`: `mir_coroutine_witnesses` and `diagnostic_hir_wf_check`. Only the query declarations need to be modified; existing providers and callers continue to work with the new query return type. (My real goal is to apply this to `coverage_ids_info`, which will return Option as of #135873, but that PR hasn't landed yet.) [^deref]: Technically they could return other types that implement `Deref`, but it's hard to imagine this working well with anything other than `&T`.
2 parents a30f915 + 4c448d5 commit e9e5291

File tree

3 files changed

+64
-9
lines changed

3 files changed

+64
-9
lines changed

Diff for: compiler/rustc_middle/src/query/arena_cached.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/// Helper trait that allows `arena_cache` queries to return `Option<&T>`
2+
/// instead of `&Option<T>`, and avoid allocating `None` in the arena.
3+
///
4+
/// An arena-cached query must be declared to return a type that implements
5+
/// this trait, i.e. either `&'tcx T` or `Option<&'tcx T>`. This trait then
6+
/// determines the types returned by the provider and stored in the arena,
7+
/// and provides a function to bridge between the three types.
8+
pub trait ArenaCached<'tcx>: Sized {
9+
/// Type that is returned by the query provider.
10+
type Provided;
11+
/// Type that is stored in the arena.
12+
type Allocated: 'tcx;
13+
14+
/// Takes a provided value, and allocates it in the arena (if appropriate)
15+
/// with the help of the given `arena_alloc` closure.
16+
fn alloc_in_arena(
17+
arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated,
18+
value: Self::Provided,
19+
) -> Self;
20+
}
21+
22+
impl<'tcx, T> ArenaCached<'tcx> for &'tcx T {
23+
type Provided = T;
24+
type Allocated = T;
25+
26+
fn alloc_in_arena(
27+
arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated,
28+
value: Self::Provided,
29+
) -> Self {
30+
// Just allocate in the arena normally.
31+
arena_alloc(value)
32+
}
33+
}
34+
35+
impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> {
36+
type Provided = Option<T>;
37+
/// The provide value is `Option<T>`, but we only store `T` in the arena.
38+
type Allocated = T;
39+
40+
fn alloc_in_arena(
41+
arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated,
42+
value: Self::Provided,
43+
) -> Self {
44+
// Don't store None in the arena, and wrap the allocated reference in Some.
45+
value.map(arena_alloc)
46+
}
47+
}

Diff for: compiler/rustc_middle/src/query/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#![allow(unused_parens)]
88

99
use std::mem;
10-
use std::ops::Deref;
1110
use std::path::PathBuf;
1211
use std::sync::Arc;
1312

@@ -85,6 +84,7 @@ use crate::ty::{
8584
};
8685
use crate::{dep_graph, mir, thir};
8786

87+
mod arena_cached;
8888
pub mod erase;
8989
mod keys;
9090
pub use keys::{AsLocalKey, Key, LocalCrate};
@@ -586,7 +586,7 @@ rustc_queries! {
586586
separate_provide_extern
587587
}
588588

589-
query mir_coroutine_witnesses(key: DefId) -> &'tcx Option<mir::CoroutineLayout<'tcx>> {
589+
query mir_coroutine_witnesses(key: DefId) -> Option<&'tcx mir::CoroutineLayout<'tcx>> {
590590
arena_cache
591591
desc { |tcx| "coroutine witness types for `{}`", tcx.def_path_str(key) }
592592
cache_on_disk_if { key.is_local() }
@@ -2415,7 +2415,7 @@ rustc_queries! {
24152415
/// because the `ty::Ty`-based wfcheck is always run.
24162416
query diagnostic_hir_wf_check(
24172417
key: (ty::Predicate<'tcx>, WellFormedLoc)
2418-
) -> &'tcx Option<ObligationCause<'tcx>> {
2418+
) -> Option<&'tcx ObligationCause<'tcx>> {
24192419
arena_cache
24202420
eval_always
24212421
no_hash

Diff for: compiler/rustc_middle/src/query/plumbing.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,10 @@ macro_rules! define_callbacks {
289289

290290
/// This type alias specifies the type returned from query providers and the type
291291
/// used for decoding. For regular queries this is the declared returned type `V`,
292-
/// but `arena_cache` will use `<V as Deref>::Target` instead.
292+
/// but `arena_cache` will use `<V as ArenaCached>::Provided` instead.
293293
pub type ProvidedValue<'tcx> = query_if_arena!(
294294
[$($modifiers)*]
295-
(<$V as Deref>::Target)
295+
(<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided)
296296
($V)
297297
);
298298

@@ -307,10 +307,18 @@ macro_rules! define_callbacks {
307307
) -> Erase<Value<'tcx>> {
308308
erase(query_if_arena!([$($modifiers)*]
309309
{
310-
if mem::needs_drop::<ProvidedValue<'tcx>>() {
311-
&*_tcx.query_system.arenas.$name.alloc(value)
310+
use $crate::query::arena_cached::ArenaCached;
311+
312+
if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() {
313+
<$V as ArenaCached>::alloc_in_arena(
314+
|v| _tcx.query_system.arenas.$name.alloc(v),
315+
value,
316+
)
312317
} else {
313-
&*_tcx.arena.dropless.alloc(value)
318+
<$V as ArenaCached>::alloc_in_arena(
319+
|v| _tcx.arena.dropless.alloc(v),
320+
value,
321+
)
314322
}
315323
}
316324
(value)
@@ -354,7 +362,7 @@ macro_rules! define_callbacks {
354362

355363
pub struct QueryArenas<'tcx> {
356364
$($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*]
357-
(TypedArena<<$V as Deref>::Target>)
365+
(TypedArena<<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated>)
358366
()
359367
),)*
360368
}

0 commit comments

Comments
 (0)