Skip to content

Memoize types in is_representable to avoid exponential worst-case #42751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 44 additions & 13 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<'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])
}
Expand Down Expand Up @@ -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<'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, _) => {
{
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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<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
}
Expand Down
55 changes: 55 additions & 0 deletions src/test/run-pass/issue-42747.rs
Original file line number Diff line number Diff line change
@@ -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;
}