Skip to content

Commit 39220a9

Browse files
committed
Auto merge of #42751 - arielb1:fast-representable, r=eddyb
Memoize types in `is_representable` to avoid exponential worst-case I could have made representability a cached query, but that would have been added complexity for not much benefit - outside of the exponential worst-case, this pass is fast enough already. Fixes #42747. r? @eddyb
2 parents 03198da + ae8545b commit 39220a9

File tree

2 files changed

+99
-13
lines changed

2 files changed

+99
-13
lines changed

src/librustc/ty/util.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use middle::lang_items;
2525
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
2626
use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
2727
HashStable};
28+
use rustc_data_structures::fx::FxHashMap;
2829
use std::cmp;
2930
use std::hash::Hash;
3031
use std::intrinsics;
@@ -835,27 +836,33 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
835836
})
836837
}
837838

838-
fn are_inner_types_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span,
839-
seen: &mut Vec<Ty<'tcx>>, ty: Ty<'tcx>)
840-
-> Representability {
839+
fn are_inner_types_recursive<'a, 'tcx>(
840+
tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span,
841+
seen: &mut Vec<Ty<'tcx>>,
842+
representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
843+
ty: Ty<'tcx>)
844+
-> Representability
845+
{
841846
match ty.sty {
842847
TyTuple(ref ts, _) => {
843848
// Find non representable
844849
fold_repr(ts.iter().map(|ty| {
845-
is_type_structurally_recursive(tcx, sp, seen, ty)
850+
is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
846851
}))
847852
}
848853
// Fixed-length vectors.
849854
// FIXME(#11924) Behavior undecided for zero-length vectors.
850855
TyArray(ty, _) => {
851-
is_type_structurally_recursive(tcx, sp, seen, ty)
856+
is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty)
852857
}
853858
TyAdt(def, substs) => {
854859
// Find non representable fields with their spans
855860
fold_repr(def.all_fields().map(|field| {
856861
let ty = field.ty(tcx, substs);
857862
let span = tcx.hir.span_if_local(field.did).unwrap_or(sp);
858-
match is_type_structurally_recursive(tcx, span, seen, ty) {
863+
match is_type_structurally_recursive(tcx, span, seen,
864+
representable_cache, ty)
865+
{
859866
Representability::SelfRecursive(_) => {
860867
Representability::SelfRecursive(vec![span])
861868
}
@@ -896,12 +903,34 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
896903

897904
// Does the type `ty` directly (without indirection through a pointer)
898905
// contain any types on stack `seen`?
899-
fn is_type_structurally_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
900-
sp: Span,
901-
seen: &mut Vec<Ty<'tcx>>,
902-
ty: Ty<'tcx>) -> Representability {
906+
fn is_type_structurally_recursive<'a, 'tcx>(
907+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
908+
sp: Span,
909+
seen: &mut Vec<Ty<'tcx>>,
910+
representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
911+
ty: Ty<'tcx>) -> Representability
912+
{
903913
debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
914+
if let Some(representability) = representable_cache.get(ty) {
915+
debug!("is_type_structurally_recursive: {:?} {:?} - (cached) {:?}",
916+
ty, sp, representability);
917+
return representability.clone();
918+
}
919+
920+
let representability = is_type_structurally_recursive_inner(
921+
tcx, sp, seen, representable_cache, ty);
922+
923+
representable_cache.insert(ty, representability.clone());
924+
representability
925+
}
904926

927+
fn is_type_structurally_recursive_inner<'a, 'tcx>(
928+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
929+
sp: Span,
930+
seen: &mut Vec<Ty<'tcx>>,
931+
representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
932+
ty: Ty<'tcx>) -> Representability
933+
{
905934
match ty.sty {
906935
TyAdt(def, _) => {
907936
{
@@ -948,13 +977,13 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
948977
// For structs and enums, track all previously seen types by pushing them
949978
// onto the 'seen' stack.
950979
seen.push(ty);
951-
let out = are_inner_types_recursive(tcx, sp, seen, ty);
980+
let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty);
952981
seen.pop();
953982
out
954983
}
955984
_ => {
956985
// No need to push in other cases.
957-
are_inner_types_recursive(tcx, sp, seen, ty)
986+
are_inner_types_recursive(tcx, sp, seen, representable_cache, ty)
958987
}
959988
}
960989
}
@@ -965,7 +994,9 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
965994
// contains a different, structurally recursive type, maintain a stack
966995
// of seen types and check recursion for each of them (issues #3008, #3779).
967996
let mut seen: Vec<Ty> = Vec::new();
968-
let r = is_type_structurally_recursive(tcx, sp, &mut seen, self);
997+
let mut representable_cache = FxHashMap();
998+
let r = is_type_structurally_recursive(
999+
tcx, sp, &mut seen, &mut representable_cache, self);
9691000
debug!("is_type_representable: {:?} is {:?}", self, r);
9701001
r
9711002
}

src/test/run-pass/issue-42747.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! fooN {
12+
($cur:ident $prev:ty) => {
13+
#[allow(dead_code)]
14+
enum $cur {
15+
Empty,
16+
First($prev),
17+
Second($prev),
18+
Third($prev),
19+
Fourth($prev),
20+
}
21+
}
22+
}
23+
24+
fooN!(Foo0 ());
25+
fooN!(Foo1 Foo0);
26+
fooN!(Foo2 Foo1);
27+
fooN!(Foo3 Foo2);
28+
fooN!(Foo4 Foo3);
29+
fooN!(Foo5 Foo4);
30+
fooN!(Foo6 Foo5);
31+
fooN!(Foo7 Foo6);
32+
fooN!(Foo8 Foo7);
33+
fooN!(Foo9 Foo8);
34+
fooN!(Foo10 Foo9);
35+
fooN!(Foo11 Foo10);
36+
fooN!(Foo12 Foo11);
37+
fooN!(Foo13 Foo12);
38+
fooN!(Foo14 Foo13);
39+
fooN!(Foo15 Foo14);
40+
fooN!(Foo16 Foo15);
41+
fooN!(Foo17 Foo16);
42+
fooN!(Foo18 Foo17);
43+
fooN!(Foo19 Foo18);
44+
fooN!(Foo20 Foo19);
45+
fooN!(Foo21 Foo20);
46+
fooN!(Foo22 Foo21);
47+
fooN!(Foo23 Foo22);
48+
fooN!(Foo24 Foo23);
49+
fooN!(Foo25 Foo24);
50+
fooN!(Foo26 Foo25);
51+
fooN!(Foo27 Foo26);
52+
53+
fn main() {
54+
let _foo = Foo27::Empty;
55+
}

0 commit comments

Comments
 (0)