about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2016-11-15 23:25:59 +0200
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2016-12-02 00:54:22 +0200
commit242cd7ebe295d44c7b612e2e1da8b83412d31f49 (patch)
treeefd32ff8f3e43532eb32d66e57c676375920885e
parent908dba0c9477b7dd022a236cb1514ddfca9369f2 (diff)
downloadrust-242cd7ebe295d44c7b612e2e1da8b83412d31f49.tar.gz
rust-242cd7ebe295d44c7b612e2e1da8b83412d31f49.zip
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.
-rw-r--r--src/librustc/middle/recursion_limit.rs21
-rw-r--r--src/librustc/session/mod.rs4
-rw-r--r--src/librustc_driver/driver.rs2
-rw-r--r--src/librustc_trans/collector.rs35
-rw-r--r--src/libsyntax/feature_gate.rs1
-rw-r--r--src/test/compile-fail/issue-22638.rs3
-rw-r--r--src/test/compile-fail/type_length_limit.rs35
-rw-r--r--src/test/ui/issue-37311-type-length-limit/issue-37311.rs30
-rw-r--r--src/test/ui/issue-37311-type-length-limit/issue-37311.stderr13
9 files changed, 137 insertions, 7 deletions
diff --git a/src/librustc/middle/recursion_limit.rs b/src/librustc/middle/recursion_limit.rs
index 7f89461a3f4..6c87f750376 100644
--- a/src/librustc/middle/recursion_limit.rs
+++ b/src/librustc/middle/recursion_limit.rs
@@ -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);
     }
 }
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 3d8cfd19961..91765e68ae6 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -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),
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index bee79103b41..069f0a89bef 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -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", || {
diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs
index 120e1a562eb..7416b86bfeb 100644
--- a/src/librustc_trans/collector.rs
+++ b/src/librustc_trans/collector.rs
@@ -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>,
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index aa6a29b78b0..2a745e979a7 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -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
diff --git a/src/test/compile-fail/issue-22638.rs b/src/test/compile-fail/issue-22638.rs
index 0c8c2311dca..65d1d837d7d 100644
--- a/src/test/compile-fail/issue-22638.rs
+++ b/src/test/compile-fail/issue-22638.rs
@@ -10,7 +10,8 @@
 
 #![allow(unused)]
 
-#![recursion_limit = "32"]
+#![recursion_limit = "20"]
+#![type_length_limit = "20000000"]
 
 #[derive(Clone)]
 struct A (B);
diff --git a/src/test/compile-fail/type_length_limit.rs b/src/test/compile-fail/type_length_limit.rs
new file mode 100644
index 00000000000..d283f392d76
--- /dev/null
+++ b/src/test/compile-fail/type_length_limit.rs
@@ -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);
+}
diff --git a/src/test/ui/issue-37311-type-length-limit/issue-37311.rs b/src/test/ui/issue-37311-type-length-limit/issue-37311.rs
new file mode 100644
index 00000000000..add96461f1b
--- /dev/null
+++ b/src/test/ui/issue-37311-type-length-limit/issue-37311.rs
@@ -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();
+}
diff --git a/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr b/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr
new file mode 100644
index 00000000000..5a63d235a7f
--- /dev/null
+++ b/src/test/ui/issue-37311-type-length-limit/issue-37311.stderr
@@ -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
+