Skip to content

Commit c1c1f05

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. Finally, the traversable derive macros are updated to utilise this specialisation machinery, thereby avoiding any need for trivially traversable fields (of types for which the traits are derived) to implement the traversable traits. [Auto-deref specialisation]: http://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
1 parent f2b7d0d commit c1c1f05

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)