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.
This commit is contained in:
Ariel Ben-Yehuda 2017-06-19 18:44:05 +03:00
parent 5ce5126199
commit ae8545bd14
2 changed files with 99 additions and 13 deletions

View file

@ -25,6 +25,7 @@
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 @@ fn fold_repr<It: Iterator<Item=Representability>>(iter: It) -> Representability
})
}
fn are_inner_types_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span,
seen: &mut Vec<Ty<'tcx>>, ty: Ty<'tcx>)
-> Representability {
fn are_inner_types_recursive<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span,
seen: &mut Vec<Ty<'tcx>>,
representable_cache: &mut FxHashMap<Ty<'tcx>, 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 @@ fn same_type<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
// 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<'tcx>>,
ty: Ty<'tcx>) -> Representability {
fn is_type_structurally_recursive<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
sp: Span,
seen: &mut Vec<Ty<'tcx>>,
representable_cache: &mut FxHashMap<Ty<'tcx>, 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<Ty<'tcx>>,
representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
ty: Ty<'tcx>) -> Representability
{
match ty.sty {
TyAdt(def, _) => {
{
@ -948,13 +977,13 @@ fn is_type_structurally_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, '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 @@ fn is_type_structurally_recursive<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, '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<Ty> = 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
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
}