mirror of
https://github.com/rust-lang/rust
synced 2024-11-02 11:53:40 +00:00
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:
parent
5ce5126199
commit
ae8545bd14
2 changed files with 99 additions and 13 deletions
|
@ -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
|
||||
}
|
||||
|
|
55
src/test/run-pass/issue-42747.rs
Normal file
55
src/test/run-pass/issue-42747.rs
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue