From ae8545bd146659f6ed4b063166e382da623bc6f8 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 19 Jun 2017 18:44:05 +0300 Subject: [PATCH] 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. --- src/librustc/ty/util.rs | 57 ++++++++++++++++++++++++-------- src/test/run-pass/issue-42747.rs | 55 ++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 src/test/run-pass/issue-42747.rs diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index a7029ac5fa9f..1bbc76734856 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -25,6 +25,7 @@ use middle::lang_items; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, HashStable}; +use rustc_data_structures::fx::FxHashMap; use std::cmp; use std::hash::Hash; use std::intrinsics; @@ -835,27 +836,33 @@ impl<'a, 'tcx> ty::TyS<'tcx> { }) } - fn are_inner_types_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, - seen: &mut Vec>, ty: Ty<'tcx>) - -> Representability { + fn are_inner_types_recursive<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, + seen: &mut Vec>, + representable_cache: &mut FxHashMap, Representability>, + ty: Ty<'tcx>) + -> Representability + { match ty.sty { TyTuple(ref ts, _) => { // Find non representable fold_repr(ts.iter().map(|ty| { - is_type_structurally_recursive(tcx, sp, seen, ty) + is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty) })) } // Fixed-length vectors. // FIXME(#11924) Behavior undecided for zero-length vectors. TyArray(ty, _) => { - is_type_structurally_recursive(tcx, sp, seen, ty) + is_type_structurally_recursive(tcx, sp, seen, representable_cache, ty) } TyAdt(def, substs) => { // Find non representable fields with their spans fold_repr(def.all_fields().map(|field| { let ty = field.ty(tcx, substs); let span = tcx.hir.span_if_local(field.did).unwrap_or(sp); - match is_type_structurally_recursive(tcx, span, seen, ty) { + match is_type_structurally_recursive(tcx, span, seen, + representable_cache, ty) + { Representability::SelfRecursive(_) => { Representability::SelfRecursive(vec![span]) } @@ -896,12 +903,34 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // Does the type `ty` directly (without indirection through a pointer) // contain any types on stack `seen`? - fn is_type_structurally_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - sp: Span, - seen: &mut Vec>, - ty: Ty<'tcx>) -> Representability { + fn is_type_structurally_recursive<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + sp: Span, + seen: &mut Vec>, + representable_cache: &mut FxHashMap, Representability>, + ty: Ty<'tcx>) -> Representability + { debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp); + if let Some(representability) = representable_cache.get(ty) { + debug!("is_type_structurally_recursive: {:?} {:?} - (cached) {:?}", + ty, sp, representability); + return representability.clone(); + } + let representability = is_type_structurally_recursive_inner( + tcx, sp, seen, representable_cache, ty); + + representable_cache.insert(ty, representability.clone()); + representability + } + + fn is_type_structurally_recursive_inner<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + sp: Span, + seen: &mut Vec>, + representable_cache: &mut FxHashMap, Representability>, + ty: Ty<'tcx>) -> Representability + { match ty.sty { TyAdt(def, _) => { { @@ -948,13 +977,13 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // For structs and enums, track all previously seen types by pushing them // onto the 'seen' stack. seen.push(ty); - let out = are_inner_types_recursive(tcx, sp, seen, ty); + let out = are_inner_types_recursive(tcx, sp, seen, representable_cache, ty); seen.pop(); out } _ => { // No need to push in other cases. - are_inner_types_recursive(tcx, sp, seen, ty) + are_inner_types_recursive(tcx, sp, seen, representable_cache, ty) } } } @@ -965,7 +994,9 @@ impl<'a, 'tcx> ty::TyS<'tcx> { // contains a different, structurally recursive type, maintain a stack // of seen types and check recursion for each of them (issues #3008, #3779). let mut seen: Vec = Vec::new(); - let r = is_type_structurally_recursive(tcx, sp, &mut seen, self); + let mut representable_cache = FxHashMap(); + let r = is_type_structurally_recursive( + tcx, sp, &mut seen, &mut representable_cache, self); debug!("is_type_representable: {:?} is {:?}", self, r); r } diff --git a/src/test/run-pass/issue-42747.rs b/src/test/run-pass/issue-42747.rs new file mode 100644 index 000000000000..05043ae6b214 --- /dev/null +++ b/src/test/run-pass/issue-42747.rs @@ -0,0 +1,55 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! fooN { + ($cur:ident $prev:ty) => { + #[allow(dead_code)] + enum $cur { + Empty, + First($prev), + Second($prev), + Third($prev), + Fourth($prev), + } + } +} + +fooN!(Foo0 ()); +fooN!(Foo1 Foo0); +fooN!(Foo2 Foo1); +fooN!(Foo3 Foo2); +fooN!(Foo4 Foo3); +fooN!(Foo5 Foo4); +fooN!(Foo6 Foo5); +fooN!(Foo7 Foo6); +fooN!(Foo8 Foo7); +fooN!(Foo9 Foo8); +fooN!(Foo10 Foo9); +fooN!(Foo11 Foo10); +fooN!(Foo12 Foo11); +fooN!(Foo13 Foo12); +fooN!(Foo14 Foo13); +fooN!(Foo15 Foo14); +fooN!(Foo16 Foo15); +fooN!(Foo17 Foo16); +fooN!(Foo18 Foo17); +fooN!(Foo19 Foo18); +fooN!(Foo20 Foo19); +fooN!(Foo21 Foo20); +fooN!(Foo22 Foo21); +fooN!(Foo23 Foo22); +fooN!(Foo24 Foo23); +fooN!(Foo25 Foo24); +fooN!(Foo26 Foo25); +fooN!(Foo27 Foo26); + +fn main() { + let _foo = Foo27::Empty; +}