From 242cd7ebe295d44c7b612e2e1da8b83412d31f49 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 15 Nov 2016 23:25:59 +0200 Subject: [PATCH] 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. --- src/librustc/middle/recursion_limit.rs | 21 ++++++++--- src/librustc/session/mod.rs | 4 +++ src/librustc_driver/driver.rs | 2 +- src/librustc_trans/collector.rs | 35 +++++++++++++++++++ src/libsyntax/feature_gate.rs | 1 + src/test/compile-fail/issue-22638.rs | 3 +- src/test/compile-fail/type_length_limit.rs | 35 +++++++++++++++++++ .../issue-37311.rs | 30 ++++++++++++++++ .../issue-37311.stderr | 13 +++++++ 9 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 src/test/compile-fail/type_length_limit.rs create mode 100644 src/test/ui/issue-37311-type-length-limit/issue-37311.rs create mode 100644 src/test/ui/issue-37311-type-length-limit/issue-37311.stderr diff --git a/src/librustc/middle/recursion_limit.rs b/src/librustc/middle/recursion_limit.rs index 7f89461a3f4b6..6c87f750376fa 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, + 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 3d8cfd199615e..91765e68ae6e1 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, + /// The maximum length of types during monomorphization. + pub type_length_limit: Cell, + /// 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 bee79103b4185..069f0a89bef08 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -566,7 +566,7 @@ pub fn phase_2_configure_and_expand(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 120e1a562ebea..7416b86bfebf3 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 aa6a29b78b075..2a745e979a72d 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 0c8c2311dcaa5..65d1d837d7dc3 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 0000000000000..d283f392d7628 --- /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 or the MIT license +// , 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::>(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 0000000000000..add96461f1bfe --- /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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Mirror { + type Image; +} + +impl Mirror for T { type Image = T; } + +trait Foo { + fn recurse(&self); +} + +impl 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 0000000000000..5a63d235a7f07 --- /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 `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(), &()), &(&()...` + --> $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 +