Skip to content

Commit 6d3acf5

Browse files
committed
Auto merge of rust-lang#76928 - lcnr:opaque-types-cache, r=tmandry
cache types during normalization partially fixes rust-lang#75992 reduces the following test from 14 to 3 seconds locally. cc `@Mark-Simulacrum` would it make sense to add that test to `perf`? ```rust #![recursion_limit="2048"] #![type_length_limit="112457564"] pub async fn h0(v: &String, x: &u64) { println!("{} {}", v, x) } pub async fn h1(v: &String, x: &u64) { h0(v, x).await } pub async fn h2(v: &String, x: &u64) { h1(v, x).await } pub async fn h3(v: &String, x: &u64) { h2(v, x).await } pub async fn h4(v: &String, x: &u64) { h3(v, x).await } pub async fn h5(v: &String, x: &u64) { h4(v, x).await } pub async fn h6(v: &String, x: &u64) { h5(v, x).await } pub async fn h7(v: &String, x: &u64) { h6(v, x).await } pub async fn h8(v: &String, x: &u64) { h7(v, x).await } pub async fn h9(v: &String, x: &u64) { h8(v, x).await } pub async fn h10(v: &String, x: &u64) { h9(v, x).await } pub async fn h11(v: &String, x: &u64) { h10(v, x).await } pub async fn h12(v: &String, x: &u64) { h11(v, x).await } pub async fn h13(v: &String, x: &u64) { h12(v, x).await } pub async fn h14(v: &String, x: &u64) { h13(v, x).await } pub async fn h15(v: &String, x: &u64) { h14(v, x).await } pub async fn h16(v: &String, x: &u64) { h15(v, x).await } pub async fn h17(v: &String, x: &u64) { h16(v, x).await } pub async fn h18(v: &String, x: &u64) { h17(v, x).await } pub async fn h19(v: &String, x: &u64) { h18(v, x).await } macro_rules! async_recursive { (29, $inner:expr) => { async { async_recursive!(28, $inner) }.await }; (28, $inner:expr) => { async { async_recursive!(27, $inner) }.await }; (27, $inner:expr) => { async { async_recursive!(26, $inner) }.await }; (26, $inner:expr) => { async { async_recursive!(25, $inner) }.await }; (25, $inner:expr) => { async { async_recursive!(24, $inner) }.await }; (24, $inner:expr) => { async { async_recursive!(23, $inner) }.await }; (23, $inner:expr) => { async { async_recursive!(22, $inner) }.await }; (22, $inner:expr) => { async { async_recursive!(21, $inner) }.await }; (21, $inner:expr) => { async { async_recursive!(20, $inner) }.await }; (20, $inner:expr) => { async { async_recursive!(19, $inner) }.await }; (19, $inner:expr) => { async { async_recursive!(18, $inner) }.await }; (18, $inner:expr) => { async { async_recursive!(17, $inner) }.await }; (17, $inner:expr) => { async { async_recursive!(16, $inner) }.await }; (16, $inner:expr) => { async { async_recursive!(15, $inner) }.await }; (15, $inner:expr) => { async { async_recursive!(14, $inner) }.await }; (14, $inner:expr) => { async { async_recursive!(13, $inner) }.await }; (13, $inner:expr) => { async { async_recursive!(12, $inner) }.await }; (12, $inner:expr) => { async { async_recursive!(11, $inner) }.await }; (11, $inner:expr) => { async { async_recursive!(10, $inner) }.await }; (10, $inner:expr) => { async { async_recursive!(9, $inner) }.await }; (9, $inner:expr) => { async { async_recursive!(8, $inner) }.await }; (8, $inner:expr) => { async { async_recursive!(7, $inner) }.await }; (7, $inner:expr) => { async { async_recursive!(6, $inner) }.await }; (6, $inner:expr) => { async { async_recursive!(5, $inner) }.await }; (5, $inner:expr) => { async { async_recursive!(4, $inner) }.await }; (4, $inner:expr) => { async { async_recursive!(3, $inner) }.await }; (3, $inner:expr) => { async { async_recursive!(2, $inner) }.await }; (2, $inner:expr) => { async { async_recursive!(1, $inner) }.await }; (1, $inner:expr) => { async { async_recursive!(0, $inner) }.await }; (0, $inner:expr) => { async { h19(&String::from("owo"), &0).await; $inner }.await }; } async fn f() { async_recursive!(14, println!("hello")); } fn main() { let _ = f(); } ``` r? `@eddyb` requires a perf run.
2 parents 0da5800 + 1146c39 commit 6d3acf5

File tree

7 files changed

+83
-68
lines changed

7 files changed

+83
-68
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3467,6 +3467,7 @@ dependencies = [
34673467
name = "rustc_data_structures"
34683468
version = "0.0.0"
34693469
dependencies = [
3470+
"arrayvec",
34703471
"bitflags",
34713472
"cfg-if",
34723473
"crossbeam-utils 0.7.2",

compiler/rustc_data_structures/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2018"
88
doctest = false
99

1010
[dependencies]
11+
arrayvec = { version = "0.5.1", default-features = false }
1112
ena = "0.14"
1213
indexmap = "1.5.1"
1314
tracing = "0.1"

compiler/rustc_data_structures/src/lib.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -88,25 +88,27 @@ pub mod sorted_map;
8888
pub mod stable_set;
8989
#[macro_use]
9090
pub mod stable_hasher;
91+
mod atomic_ref;
92+
pub mod fingerprint;
93+
pub mod profiling;
9194
pub mod sharded;
9295
pub mod stack;
9396
pub mod sync;
9497
pub mod thin_vec;
9598
pub mod tiny_list;
9699
pub mod transitive_relation;
97-
pub use ena::undo_log;
98-
pub use ena::unify;
99-
mod atomic_ref;
100-
pub mod fingerprint;
101-
pub mod profiling;
102100
pub mod vec_linked_list;
103101
pub mod work_queue;
104102
pub use atomic_ref::AtomicRef;
105103
pub mod frozen;
104+
pub mod mini_map;
106105
pub mod tagged_ptr;
107106
pub mod temp_dir;
108107
pub mod unhash;
109108

109+
pub use ena::undo_log;
110+
pub use ena::unify;
111+
110112
pub struct OnDrop<F: Fn()>(pub F);
111113

112114
impl<F: Fn()> OnDrop<F> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use crate::fx::FxHashMap;
2+
use arrayvec::ArrayVec;
3+
4+
use std::hash::Hash;
5+
6+
/// Small-storage-optimized implementation of a map
7+
/// made specifically for caching results.
8+
///
9+
/// Stores elements in a small array up to a certain length
10+
/// and switches to `HashMap` when that length is exceeded.
11+
pub enum MiniMap<K, V> {
12+
Array(ArrayVec<[(K, V); 8]>),
13+
Map(FxHashMap<K, V>),
14+
}
15+
16+
impl<K: Eq + Hash, V> MiniMap<K, V> {
17+
/// Creates an empty `MiniMap`.
18+
pub fn new() -> Self {
19+
MiniMap::Array(ArrayVec::new())
20+
}
21+
22+
/// Inserts or updates value in the map.
23+
pub fn insert(&mut self, key: K, value: V) {
24+
match self {
25+
MiniMap::Array(array) => {
26+
for pair in array.iter_mut() {
27+
if pair.0 == key {
28+
pair.1 = value;
29+
return;
30+
}
31+
}
32+
if let Err(error) = array.try_push((key, value)) {
33+
let mut map: FxHashMap<K, V> = array.drain(..).collect();
34+
let (key, value) = error.element();
35+
map.insert(key, value);
36+
*self = MiniMap::Map(map);
37+
}
38+
}
39+
MiniMap::Map(map) => {
40+
map.insert(key, value);
41+
}
42+
}
43+
}
44+
45+
/// Return value by key if any.
46+
pub fn get(&self, key: &K) -> Option<&V> {
47+
match self {
48+
MiniMap::Array(array) => {
49+
for pair in array {
50+
if pair.0 == *key {
51+
return Some(&pair.1);
52+
}
53+
}
54+
return None;
55+
}
56+
MiniMap::Map(map) => {
57+
return map.get(key);
58+
}
59+
}
60+
}
61+
}

compiler/rustc_index/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ edition = "2018"
88
doctest = false
99

1010
[dependencies]
11-
arrayvec = "0.5.1"
11+
arrayvec = { version = "0.5.1", default-features = false }
1212
rustc_serialize = { path = "../rustc_serialize" }
1313
rustc_macros = { path = "../rustc_macros" }

compiler/rustc_infer/src/infer/combine.rs

+1-60
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,11 @@ use super::unify_key::replace_if_possible;
3131
use super::unify_key::{ConstVarValue, ConstVariableValue};
3232
use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
3333
use super::{InferCtxt, MiscVariable, TypeTrace};
34-
use arrayvec::ArrayVec;
35-
use rustc_data_structures::fx::FxHashMap;
36-
use std::hash::Hash;
3734

3835
use crate::traits::{Obligation, PredicateObligations};
3936

4037
use rustc_ast as ast;
38+
use rustc_data_structures::mini_map::MiniMap;
4139
use rustc_hir::def_id::DefId;
4240
use rustc_middle::traits::ObligationCause;
4341
use rustc_middle::ty::error::TypeError;
@@ -47,63 +45,6 @@ use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
4745
use rustc_middle::ty::{IntType, UintType};
4846
use rustc_span::{Span, DUMMY_SP};
4947

50-
/// Small-storage-optimized implementation of a map
51-
/// made specifically for caching results.
52-
///
53-
/// Stores elements in a small array up to a certain length
54-
/// and switches to `HashMap` when that length is exceeded.
55-
enum MiniMap<K, V> {
56-
Array(ArrayVec<[(K, V); 8]>),
57-
Map(FxHashMap<K, V>),
58-
}
59-
60-
impl<K: Eq + Hash, V> MiniMap<K, V> {
61-
/// Creates an empty `MiniMap`.
62-
pub fn new() -> Self {
63-
MiniMap::Array(ArrayVec::new())
64-
}
65-
66-
/// Inserts or updates value in the map.
67-
pub fn insert(&mut self, key: K, value: V) {
68-
match self {
69-
MiniMap::Array(array) => {
70-
for pair in array.iter_mut() {
71-
if pair.0 == key {
72-
pair.1 = value;
73-
return;
74-
}
75-
}
76-
if let Err(error) = array.try_push((key, value)) {
77-
let mut map: FxHashMap<K, V> = array.drain(..).collect();
78-
let (key, value) = error.element();
79-
map.insert(key, value);
80-
*self = MiniMap::Map(map);
81-
}
82-
}
83-
MiniMap::Map(map) => {
84-
map.insert(key, value);
85-
}
86-
}
87-
}
88-
89-
/// Return value by key if any.
90-
pub fn get(&self, key: &K) -> Option<&V> {
91-
match self {
92-
MiniMap::Array(array) => {
93-
for pair in array {
94-
if pair.0 == *key {
95-
return Some(&pair.1);
96-
}
97-
}
98-
return None;
99-
}
100-
MiniMap::Map(map) => {
101-
return map.get(key);
102-
}
103-
}
104-
}
105-
}
106-
10748
#[derive(Clone)]
10849
pub struct CombineFields<'infcx, 'tcx> {
10950
pub infcx: &'infcx InferCtxt<'infcx, 'tcx>,

compiler/rustc_trait_selection/src/traits/query/normalize.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::infer::canonical::OriginalQueryValues;
77
use crate::infer::{InferCtxt, InferOk};
88
use crate::traits::error_reporting::InferCtxtExt;
99
use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
10+
use rustc_data_structures::mini_map::MiniMap;
1011
use rustc_data_structures::stack::ensure_sufficient_stack;
1112
use rustc_infer::traits::Normalized;
1213
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
@@ -57,6 +58,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
5758
param_env: self.param_env,
5859
obligations: vec![],
5960
error: false,
61+
cache: MiniMap::new(),
6062
anon_depth: 0,
6163
};
6264

@@ -85,6 +87,7 @@ struct QueryNormalizer<'cx, 'tcx> {
8587
cause: &'cx ObligationCause<'tcx>,
8688
param_env: ty::ParamEnv<'tcx>,
8789
obligations: Vec<PredicateObligation<'tcx>>,
90+
cache: MiniMap<Ty<'tcx>, Ty<'tcx>>,
8891
error: bool,
8992
anon_depth: usize,
9093
}
@@ -99,8 +102,12 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
99102
return ty;
100103
}
101104

105+
if let Some(ty) = self.cache.get(&ty) {
106+
return ty;
107+
}
108+
102109
let ty = ty.super_fold_with(self);
103-
match *ty.kind() {
110+
let res = (|| match *ty.kind() {
104111
ty::Opaque(def_id, substs) => {
105112
// Only normalize `impl Trait` after type-checking, usually in codegen.
106113
match self.param_env.reveal() {
@@ -197,7 +204,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
197204
}
198205

199206
_ => ty,
200-
}
207+
})();
208+
self.cache.insert(ty, res);
209+
res
201210
}
202211

203212
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {

0 commit comments

Comments
 (0)