Auto merge of #37789 - arielb1:length-limit, r=nikomatsakis
limit the length of types in monomorphization This adds the new insta-stable `#![type_size_limit]` crate attribute to control the limit, and is obviously a [breaking-change] fixable by that. Fixes #37311. r? @nikomatsakis
This commit is contained in:
commit
1077149827
|
@ -18,20 +18,31 @@
|
|||
use session::Session;
|
||||
use syntax::ast;
|
||||
|
||||
pub fn update_recursion_limit(sess: &Session, krate: &ast::Crate) {
|
||||
use std::cell::Cell;
|
||||
|
||||
pub fn update_limits(sess: &Session, krate: &ast::Crate) {
|
||||
update_limit(sess, krate, &sess.recursion_limit, "recursion_limit",
|
||||
"recursion limit");
|
||||
update_limit(sess, krate, &sess.type_length_limit, "type_length_limit",
|
||||
"type length limit");
|
||||
}
|
||||
|
||||
fn update_limit(sess: &Session, krate: &ast::Crate, limit: &Cell<usize>,
|
||||
name: &str, description: &str) {
|
||||
for attr in &krate.attrs {
|
||||
if !attr.check_name("recursion_limit") {
|
||||
if !attr.check_name(name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(s) = attr.value_str() {
|
||||
if let Some(n) = s.as_str().parse().ok() {
|
||||
sess.recursion_limit.set(n);
|
||||
limit.set(n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
span_err!(sess, attr.span, E0296, "malformed recursion limit attribute, \
|
||||
expected #![recursion_limit=\"N\"]");
|
||||
span_err!(sess, attr.span, E0296,
|
||||
"malformed {} attribute, expected #![{}=\"N\"]",
|
||||
description, name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,9 @@ pub struct Session {
|
|||
/// operations such as auto-dereference and monomorphization.
|
||||
pub recursion_limit: Cell<usize>,
|
||||
|
||||
/// The maximum length of types during monomorphization.
|
||||
pub type_length_limit: Cell<usize>,
|
||||
|
||||
/// The metadata::creader module may inject an allocator/panic_runtime
|
||||
/// dependency if it didn't already find one, and this tracks what was
|
||||
/// injected.
|
||||
|
@ -620,6 +623,7 @@ pub fn build_session_(sopts: config::Options,
|
|||
crate_disambiguator: RefCell::new(Symbol::intern("")),
|
||||
features: RefCell::new(feature_gate::Features::new()),
|
||||
recursion_limit: Cell::new(64),
|
||||
type_length_limit: Cell::new(1048576),
|
||||
next_node_id: Cell::new(NodeId::new(1)),
|
||||
injected_allocator: Cell::new(None),
|
||||
injected_panic_runtime: Cell::new(None),
|
||||
|
|
|
@ -566,7 +566,7 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
|
|||
*sess.crate_disambiguator.borrow_mut() = Symbol::intern(&compute_crate_disambiguator(sess));
|
||||
|
||||
time(time_passes, "recursion limit", || {
|
||||
middle::recursion_limit::update_recursion_limit(sess, &krate);
|
||||
middle::recursion_limit::update_limits(sess, &krate);
|
||||
});
|
||||
|
||||
krate = time(time_passes, "crate injection", || {
|
||||
|
|
|
@ -361,6 +361,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
|
|||
recursion_depth_reset = Some(check_recursion_limit(scx.tcx(),
|
||||
instance,
|
||||
recursion_depths));
|
||||
check_type_length_limit(scx.tcx(), instance);
|
||||
|
||||
// Scan the MIR in order to find function calls, closures, and
|
||||
// drop-glue
|
||||
|
@ -432,6 +433,40 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
(instance.def, recursion_depth)
|
||||
}
|
||||
|
||||
fn check_type_length_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
instance: Instance<'tcx>)
|
||||
{
|
||||
let type_length = instance.substs.types().flat_map(|ty| ty.walk()).count();
|
||||
debug!(" => type length={}", type_length);
|
||||
|
||||
// Rust code can easily create exponentially-long types using only a
|
||||
// polynomial recursion depth. Even with the default recursion
|
||||
// depth, you can easily get cases that take >2^60 steps to run,
|
||||
// which means that rustc basically hangs.
|
||||
//
|
||||
// Bail out in these cases to avoid that bad user experience.
|
||||
let type_length_limit = tcx.sess.type_length_limit.get();
|
||||
if type_length > type_length_limit {
|
||||
// The instance name is already known to be too long for rustc. Use
|
||||
// `{:.64}` to avoid blasting the user's terminal with thousands of
|
||||
// lines of type-name.
|
||||
let instance_name = instance.to_string();
|
||||
let msg = format!("reached the type-length limit while instantiating `{:.64}...`",
|
||||
instance_name);
|
||||
let mut diag = if let Some(node_id) = tcx.map.as_local_node_id(instance.def) {
|
||||
tcx.sess.struct_span_fatal(tcx.map.span(node_id), &msg)
|
||||
} else {
|
||||
tcx.sess.struct_fatal(&msg)
|
||||
};
|
||||
|
||||
diag.note(&format!(
|
||||
"consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate",
|
||||
type_length_limit*2));
|
||||
diag.emit();
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
|
||||
struct MirNeighborCollector<'a, 'tcx: 'a> {
|
||||
scx: &'a SharedCrateContext<'a, 'tcx>,
|
||||
mir: &'a mir::Mir<'tcx>,
|
||||
|
|
|
@ -738,6 +738,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
|||
("no_main", CrateLevel, Ungated),
|
||||
("no_builtins", CrateLevel, Ungated),
|
||||
("recursion_limit", CrateLevel, Ungated),
|
||||
("type_length_limit", CrateLevel, Ungated),
|
||||
];
|
||||
|
||||
// cfg(...)'s that are feature gated
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
#![allow(unused)]
|
||||
|
||||
#![recursion_limit = "32"]
|
||||
#![recursion_limit = "20"]
|
||||
#![type_length_limit = "20000000"]
|
||||
|
||||
#[derive(Clone)]
|
||||
struct A (B);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
// error-pattern: reached the type-length limit while instantiating
|
||||
|
||||
// Test that the type length limit can be changed.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![type_length_limit="256"]
|
||||
|
||||
macro_rules! link {
|
||||
($id:ident, $t:ty) => {
|
||||
pub type $id = ($t, $t, $t);
|
||||
}
|
||||
}
|
||||
|
||||
link! { A, B }
|
||||
link! { B, C }
|
||||
link! { C, D }
|
||||
link! { D, E }
|
||||
link! { E, F }
|
||||
link! { F, G }
|
||||
|
||||
pub struct G;
|
||||
|
||||
fn main() {
|
||||
drop::<Option<A>>(None);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
trait Mirror {
|
||||
type Image;
|
||||
}
|
||||
|
||||
impl<T> Mirror for T { type Image = T; }
|
||||
|
||||
trait Foo {
|
||||
fn recurse(&self);
|
||||
}
|
||||
|
||||
impl<T> Foo for T {
|
||||
#[allow(unconditional_recursion)]
|
||||
fn recurse(&self) {
|
||||
(self, self).recurse();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
().recurse();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
error: reached the type-length limit while instantiating `<T as Foo><(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(), &()), &(&()...`
|
||||
--> $DIR/issue-37311.rs:23:5
|
||||
|
|
||||
23 | fn recurse(&self) {
|
||||
| _____^ starting here...
|
||||
24 | | (self, self).recurse();
|
||||
25 | | }
|
||||
| |_____^ ...ending here
|
||||
|
|
||||
= note: consider adding a `#![type_length_limit="2097152"]` attribute to your crate
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue