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);
}
}
/// 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.
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),
*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", || {
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
(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>,
("no_main", CrateLevel, Ungated),
("no_builtins", CrateLevel, Ungated),
("recursion_limit", CrateLevel, Ungated),
+ ("type_length_limit", CrateLevel, Ungated),
];
// cfg(...)'s that are feature gated
#![allow(unused)]
-#![recursion_limit = "32"]
+#![recursion_limit = "20"]
+#![type_length_limit = "20000000"]
#[derive(Clone)]
struct A (B);
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+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
+