Skip to content

Commit 8e1d888

Browse files
committed
Auto merge of rust-lang#139768 - compiler-errors:split-fold, r=<try>
[Experiment] Split `TypeFolder` and `FallibleTypeFolder` r? `@ghost`
2 parents 092a284 + b960283 commit 8e1d888

File tree

11 files changed

+395
-95
lines changed

11 files changed

+395
-95
lines changed

compiler/rustc_infer/src/traits/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::hash::{Hash, Hasher};
1212

1313
use hir::def_id::LocalDefId;
1414
use rustc_hir as hir;
15+
use rustc_macros::{TypeFoldable, TypeVisitable};
1516
use rustc_middle::traits::query::NoSolution;
1617
use rustc_middle::traits::solve::Certainty;
1718
pub use rustc_middle::traits::*;
@@ -35,9 +36,11 @@ use crate::infer::InferCtxt;
3536
/// either identifying an `impl` (e.g., `impl Eq for i32`) that
3637
/// satisfies the obligation, or else finding a bound that is in
3738
/// scope. The eventual result is usually a `Selection` (defined below).
38-
#[derive(Clone)]
39+
#[derive(Clone, TypeFoldable, TypeVisitable)]
3940
pub struct Obligation<'tcx, T> {
4041
/// The reason we have to prove this thing.
42+
#[type_foldable(identity)]
43+
#[type_visitable(ignore)]
4144
pub cause: ObligationCause<'tcx>,
4245

4346
/// The environment in which we should prove this thing.
@@ -51,6 +54,8 @@ pub struct Obligation<'tcx, T> {
5154
/// If this goes over a certain threshold, we abort compilation --
5255
/// in such cases, we can not say whether or not the predicate
5356
/// holds for certain. Stupid halting problem; such a drag.
57+
#[type_foldable(identity)]
58+
#[type_visitable(ignore)]
5459
pub recursion_depth: usize,
5560
}
5661

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::fmt;
22

3-
use rustc_middle::ty::{
4-
self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitor, try_visit,
5-
};
3+
use rustc_middle::ty;
64

75
use crate::traits;
86
use crate::traits::project::Normalized;
@@ -34,31 +32,3 @@ impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> {
3432
write!(f, "MismatchedProjectionTypes({:?})", self.err)
3533
}
3634
}
37-
38-
///////////////////////////////////////////////////////////////////////////
39-
// TypeFoldable implementations.
40-
41-
impl<'tcx, O: TypeFoldable<TyCtxt<'tcx>>> TypeFoldable<TyCtxt<'tcx>>
42-
for traits::Obligation<'tcx, O>
43-
{
44-
fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
45-
self,
46-
folder: &mut F,
47-
) -> Result<Self, F::Error> {
48-
Ok(traits::Obligation {
49-
cause: self.cause,
50-
recursion_depth: self.recursion_depth,
51-
predicate: self.predicate.try_fold_with(folder)?,
52-
param_env: self.param_env.try_fold_with(folder)?,
53-
})
54-
}
55-
}
56-
57-
impl<'tcx, O: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>>
58-
for traits::Obligation<'tcx, O>
59-
{
60-
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
61-
try_visit!(self.predicate.visit_with(visitor));
62-
self.param_env.visit_with(visitor)
63-
}
64-
}

compiler/rustc_macros/src/type_foldable.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub(super) fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_m
1414

1515
s.add_bounds(synstructure::AddBounds::Generics);
1616
s.bind_with(|_| synstructure::BindStyle::Move);
17-
let body_fold = s.each_variant(|vi| {
17+
let try_body_fold = s.each_variant(|vi| {
1818
let bindings = vi.bindings();
1919
vi.construct(|_, index| {
2020
let bind = &bindings[index];
@@ -44,14 +44,51 @@ pub(super) fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_m
4444
})
4545
});
4646

47+
let body_fold = s.each_variant(|vi| {
48+
let bindings = vi.bindings();
49+
vi.construct(|_, index| {
50+
let bind = &bindings[index];
51+
52+
let mut fixed = false;
53+
54+
// retain value of fields with #[type_foldable(identity)]
55+
bind.ast().attrs.iter().for_each(|x| {
56+
if !x.path().is_ident("type_foldable") {
57+
return;
58+
}
59+
let _ = x.parse_nested_meta(|nested| {
60+
if nested.path.is_ident("identity") {
61+
fixed = true;
62+
}
63+
Ok(())
64+
});
65+
});
66+
67+
if fixed {
68+
bind.to_token_stream()
69+
} else {
70+
quote! {
71+
::rustc_middle::ty::TypeFoldable::fold_with(#bind, __folder)
72+
}
73+
}
74+
})
75+
});
76+
4777
s.bound_impl(
4878
quote!(::rustc_middle::ty::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>>),
4979
quote! {
5080
fn try_fold_with<__F: ::rustc_middle::ty::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(
5181
self,
5282
__folder: &mut __F
5383
) -> Result<Self, __F::Error> {
54-
Ok(match self { #body_fold })
84+
Ok(match self { #try_body_fold })
85+
}
86+
87+
fn fold_with<__F: ::rustc_middle::ty::TypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(
88+
self,
89+
__folder: &mut __F
90+
) -> Self {
91+
match self { #body_fold }
5592
}
5693
},
5794
)

compiler/rustc_middle/src/infer/canonical.rs

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ impl<'tcx> ty::TypeFoldable<TyCtxt<'tcx>> for CanonicalVarInfos<'tcx> {
4444
self,
4545
folder: &mut F,
4646
) -> Result<Self, F::Error> {
47+
ty::util::try_fold_list(self, folder, |tcx, v| tcx.mk_canonical_var_infos(v))
48+
}
49+
50+
fn fold_with<F: ty::TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
4751
ty::util::fold_list(self, folder, |tcx, v| tcx.mk_canonical_var_infos(v))
4852
}
4953
}

compiler/rustc_middle/src/ty/generic_args.rs

+65-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use smallvec::SmallVec;
1616
use crate::ty::codec::{TyDecoder, TyEncoder};
1717
use crate::ty::{
1818
self, ClosureArgs, CoroutineArgs, CoroutineClosureArgs, FallibleTypeFolder, InlineConstArgs,
19-
Lift, List, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitor, VisitorResult,
19+
Lift, List, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, VisitorResult,
2020
walk_visitable_list,
2121
};
2222

@@ -322,6 +322,14 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArg<'tcx> {
322322
GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
323323
}
324324
}
325+
326+
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
327+
match self.unpack() {
328+
GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(),
329+
GenericArgKind::Type(ty) => ty.fold_with(folder).into(),
330+
GenericArgKind::Const(ct) => ct.fold_with(folder).into(),
331+
}
332+
}
325333
}
326334

327335
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for GenericArg<'tcx> {
@@ -591,6 +599,32 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for GenericArgsRef<'tcx> {
591599
}
592600
}
593601
0 => Ok(self),
602+
_ => ty::util::try_fold_list(self, folder, |tcx, v| tcx.mk_args(v)),
603+
}
604+
}
605+
606+
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
607+
// This code is hot enough that it's worth specializing for the most
608+
// common length lists, to avoid the overhead of `SmallVec` creation.
609+
// The match arms are in order of frequency. The 1, 2, and 0 cases are
610+
// typically hit in 90--99.99% of cases. When folding doesn't change
611+
// the args, it's faster to reuse the existing args rather than
612+
// calling `mk_args`.
613+
match self.len() {
614+
1 => {
615+
let param0 = self[0].fold_with(folder);
616+
if param0 == self[0] { self } else { folder.cx().mk_args(&[param0]) }
617+
}
618+
2 => {
619+
let param0 = self[0].fold_with(folder);
620+
let param1 = self[1].fold_with(folder);
621+
if param0 == self[0] && param1 == self[1] {
622+
self
623+
} else {
624+
folder.cx().mk_args(&[param0, param1])
625+
}
626+
}
627+
0 => self,
594628
_ => ty::util::fold_list(self, folder, |tcx, v| tcx.mk_args(v)),
595629
}
596630
}
@@ -626,6 +660,36 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx>> {
626660
Ok(folder.cx().mk_type_list(&[param0, param1]))
627661
}
628662
}
663+
_ => ty::util::try_fold_list(self, folder, |tcx, v| tcx.mk_type_list(v)),
664+
}
665+
}
666+
667+
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
668+
// This code is fairly hot, though not as hot as `GenericArgsRef`.
669+
//
670+
// When compiling stage 2, I get the following results:
671+
//
672+
// len | total | %
673+
// --- | --------- | -----
674+
// 2 | 15083590 | 48.1
675+
// 3 | 7540067 | 24.0
676+
// 1 | 5300377 | 16.9
677+
// 4 | 1351897 | 4.3
678+
// 0 | 1256849 | 4.0
679+
//
680+
// I've tried it with some private repositories and got
681+
// close to the same result, with 4 and 0 swapping places
682+
// sometimes.
683+
match self.len() {
684+
2 => {
685+
let param0 = self[0].fold_with(folder);
686+
let param1 = self[1].fold_with(folder);
687+
if param0 == self[0] && param1 == self[1] {
688+
self
689+
} else {
690+
folder.cx().mk_type_list(&[param0, param1])
691+
}
692+
}
629693
_ => ty::util::fold_list(self, folder, |tcx, v| tcx.mk_type_list(v)),
630694
}
631695
}

compiler/rustc_middle/src/ty/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,13 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Term<'tcx> {
539539
ty::TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
540540
}
541541
}
542+
543+
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
544+
match self.unpack() {
545+
ty::TermKind::Ty(ty) => ty.fold_with(folder).into(),
546+
ty::TermKind::Const(ct) => ct.fold_with(folder).into(),
547+
}
548+
}
542549
}
543550

544551
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Term<'tcx> {

0 commit comments

Comments
 (0)