Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 63f7ed7

Browse files
committed
Add specialisation machinery for trivial traversal
[Auto-deref specialisation] is introduced to type library traversals, the public API of which is via the `TriviallyTraverses` trait and the `noop_if_trivially_traversable` macro. Traversals invoked via the macro will then be no-ops if the interner "trivially traverses" the type being traversed *without requiring that type to implement the relevant traversable trait*. A further trait, `rustc_middle::ty::TriviallyTraversable`, is then auto-implemented for types that do not contain anything that may be of interest to traversers. A generic implementation of the former trait is then provided for the `TyCtxt` interner to indicate that it "trivially traverses" all such `TriviallyTraversable` types. This indirection is necessary because auto-traits are unstable, whereas the type library is intended to be stabilised. [Auto-deref specialisation]: http://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
1 parent f2b7d0d commit 63f7ed7

File tree

8 files changed

+152
-4
lines changed

8 files changed

+152
-4
lines changed

compiler/rustc_middle/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#![feature(trait_alias)]
6565
#![feature(ptr_alignment_type)]
6666
#![feature(macro_metavar_expr)]
67+
#![feature(auto_traits)]
6768
#![recursion_limit = "512"]
6869
#![allow(rustc::potential_query_instability)]
6970
#![allow(internal_features)]

compiler/rustc_middle/src/ty/context.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}
6767
use rustc_target::spec::abi;
6868
use rustc_type_ir::TyKind::*;
6969
use rustc_type_ir::WithCachedTypeInfo;
70-
use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
70+
use rustc_type_ir::{CollectAndApply, Interner, TriviallyTraverses, TypeFlags};
7171

7272
use std::any::Any;
7373
use std::borrow::Borrow;
@@ -135,6 +135,22 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
135135
}
136136
}
137137

138+
/// Marker trait for types that do not need to be traversed by folders or visitors,
139+
/// because they do not contain anything that could be of interest.
140+
///
141+
/// Manually implementing this trait is DANGEROUS and should NEVER be done, as it
142+
/// can lead to miscompilation. Even if the type for which you wish to implement
143+
/// this trait does not today contain anything of interest to folders or visitors,
144+
/// a field added or changed in future may cause breakage.
145+
pub auto trait TriviallyTraversable {}
146+
impl<T: ?Sized + TriviallyTraversable> TriviallyTraverses<T> for TyCtxt<'_> {}
147+
148+
impl<T> !TriviallyTraversable for Binder<'_, T> {}
149+
impl !TriviallyTraversable for Ty<'_> {}
150+
impl !TriviallyTraversable for ty::Const<'_> {}
151+
impl !TriviallyTraversable for Region<'_> {}
152+
impl !TriviallyTraversable for Predicate<'_> {}
153+
138154
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
139155

140156
pub struct CtxtInterners<'tcx> {

compiler/rustc_middle/src/ty/list.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ extern "C" {
4545
type OpaqueListContents;
4646
}
4747

48+
// Auto-traits are not automatically implemented for extern types[1], so
49+
// we manually implement it here to ensure that `List`s are automatically
50+
// skipped when appropriate (the `data` field will still ensure the auto
51+
// trait is not implemented for the `List` when it is not appropriate).
52+
//
53+
// [1]: https://github.com/rust-lang/rust/issues/43467#issuecomment-1207257995
54+
impl super::TriviallyTraversable for OpaqueListContents {}
55+
4856
impl<T> List<T> {
4957
/// Returns a reference to the (unique, static) empty list.
5058
#[inline(always)]

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ pub use self::consts::{
8888
Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
8989
};
9090
pub use self::context::{
91-
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed,
91+
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TriviallyTraversable,
92+
TyCtxt, TyCtxtFeed,
9293
};
9394
pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams};
9495
pub use self::list::List;

compiler/rustc_type_ir/src/fold.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@
4747
4848
use rustc_data_structures::sync::Lrc;
4949
use rustc_index::{Idx, IndexVec};
50+
use std::marker::PhantomData;
5051
use std::mem;
5152

52-
use crate::{visit::TypeVisitable, Interner};
53+
use crate::{visit::TypeVisitable, Interner, TriviallyTraverses};
5354

5455
/// This trait is implemented for every type that can be folded,
5556
/// providing the skeleton of the traversal.
@@ -248,6 +249,44 @@ where
248249
}
249250
}
250251

252+
pub trait SpecTypeFoldable {
253+
type Interner: Interner;
254+
type Item;
255+
fn spec_try_fold_with<F: FallibleTypeFolder<Self::Interner>>(
256+
self,
257+
value: Self::Item,
258+
folder: &mut F,
259+
) -> Result<Self::Item, F::Error>;
260+
}
261+
262+
impl<I: Interner, T: TypeFoldable<I>> SpecTypeFoldable for PhantomData<(I, T)> {
263+
type Interner = I;
264+
type Item = T;
265+
266+
#[inline(always)]
267+
fn spec_try_fold_with<F: FallibleTypeFolder<I>>(
268+
self,
269+
value: T,
270+
folder: &mut F,
271+
) -> Result<T, F::Error> {
272+
value.try_fold_with(folder)
273+
}
274+
}
275+
276+
impl<I: TriviallyTraverses<T>, T> SpecTypeFoldable for &PhantomData<(I, T)> {
277+
type Interner = I;
278+
type Item = T;
279+
280+
#[inline(always)]
281+
fn spec_try_fold_with<F: FallibleTypeFolder<I>>(
282+
self,
283+
value: T,
284+
_: &mut F,
285+
) -> Result<T, F::Error> {
286+
Ok(value)
287+
}
288+
}
289+
251290
///////////////////////////////////////////////////////////////////////////
252291
// Traversal implementations.
253292

compiler/rustc_type_ir/src/interner.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,25 @@ pub trait Interner: Sized {
6666
fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability);
6767
}
6868

69+
/// Marker to signify that the interner type `Self` trivially traverses type `T`,
70+
/// which is to say that traversing (folding or visiting) values of type `T` is
71+
/// *guaranteed* to be a no-op because `T` does not contain anything that could be
72+
/// of interest to a traverser (folder or visitor). Per the traverser traits'
73+
/// methods, traversers are only capable of taking an interest in the following
74+
/// five types:
75+
///
76+
/// * `Self::Binder<B>` for any type `B`
77+
/// * `Self::Ty`
78+
/// * `Self::Region`
79+
/// * `Self::Const`
80+
/// * `Self::Predicate`
81+
//
82+
// If and when implementations of the super-traverser traits are uplifted to
83+
// this library, the `B` above will likely be restricted to types of interest.
84+
// For now, that is an implementation detail of `rustc_middle` and is therefore
85+
// omitted from this library's documentation.
86+
pub trait TriviallyTraverses<T: ?Sized>: Interner {}
87+
6988
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
7089
/// that produces `T` items. You could combine them with
7190
/// `f(&iter.collect::<Vec<_>>())`, but this requires allocating memory for the

compiler/rustc_type_ir/src/macros.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,32 @@ TrivialTypeTraversalImpls! {
5252
crate::AliasRelationDirection,
5353
crate::UniverseIndex,
5454
}
55+
56+
#[macro_export]
57+
macro_rules! noop_if_trivially_traversable {
58+
($val:tt.try_fold_with::<$interner:ty>($folder:expr)) => {{
59+
use $crate::fold::SpecTypeFoldable as _;
60+
$crate::noop_if_trivially_traversable!($val.spec_try_fold_with::<$interner>($folder))
61+
}};
62+
($val:tt.visit_with::<$interner:ty>($visitor:expr)) => {{
63+
use $crate::visit::SpecTypeVisitable as _;
64+
$crate::noop_if_trivially_traversable!($val.spec_visit_with::<$interner>($visitor))
65+
}};
66+
($val:tt.$method:ident::<$interner:ty>($traverser:expr)) => {{
67+
let val = $val;
68+
69+
#[allow(unreachable_code)]
70+
let p = 'p: {
71+
use ::core::marker::PhantomData;
72+
73+
fn unreachable_phantom_constraint<I, T>(_: T) -> PhantomData<(I, T)> {
74+
unreachable!()
75+
}
76+
77+
break 'p PhantomData;
78+
unreachable_phantom_constraint::<$interner, _>(val)
79+
};
80+
81+
(&&p).$method(val, $traverser)
82+
}};
83+
}

compiler/rustc_type_ir/src/visit.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@
4444
use rustc_data_structures::sync::Lrc;
4545
use rustc_index::{Idx, IndexVec};
4646
use std::fmt;
47+
use std::marker::PhantomData;
4748
use std::ops::ControlFlow;
4849

49-
use crate::Interner;
50+
use crate::{Interner, TriviallyTraverses};
5051

5152
/// This trait is implemented for every type that can be visited,
5253
/// providing the skeleton of the traversal.
@@ -120,6 +121,40 @@ pub trait TypeVisitor<I: Interner>: Sized {
120121
}
121122
}
122123

124+
pub trait SpecTypeVisitable {
125+
type Interner: Interner;
126+
type Item: ?Sized;
127+
fn spec_visit_with<V: TypeVisitor<Self::Interner>>(
128+
self,
129+
value: &Self::Item,
130+
visitor: &mut V,
131+
) -> ControlFlow<V::BreakTy>;
132+
}
133+
134+
impl<I: Interner, T: ?Sized + TypeVisitable<I>> SpecTypeVisitable for PhantomData<(I, &T)> {
135+
type Interner = I;
136+
type Item = T;
137+
138+
#[inline(always)]
139+
fn spec_visit_with<V: TypeVisitor<I>>(
140+
self,
141+
value: &T,
142+
visitor: &mut V,
143+
) -> ControlFlow<V::BreakTy> {
144+
value.visit_with(visitor)
145+
}
146+
}
147+
148+
impl<I: TriviallyTraverses<T>, T: ?Sized> SpecTypeVisitable for &PhantomData<(I, &T)> {
149+
type Interner = I;
150+
type Item = T;
151+
152+
#[inline(always)]
153+
fn spec_visit_with<V: TypeVisitor<I>>(self, _: &T, _: &mut V) -> ControlFlow<V::BreakTy> {
154+
ControlFlow::Continue(())
155+
}
156+
}
157+
123158
///////////////////////////////////////////////////////////////////////////
124159
// Traversal implementations.
125160

0 commit comments

Comments
 (0)