Skip to content

Commit 1077149

Browse files
committed
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
2 parents 908dba0 + 242cd7e commit 1077149

File tree

9 files changed

+137
-7
lines changed

9 files changed

+137
-7
lines changed

src/librustc/middle/recursion_limit.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,31 @@
1818
use session::Session;
1919
use syntax::ast;
2020

21-
pub fn update_recursion_limit(sess: &Session, krate: &ast::Crate) {
21+
use std::cell::Cell;
22+
23+
pub fn update_limits(sess: &Session, krate: &ast::Crate) {
24+
update_limit(sess, krate, &sess.recursion_limit, "recursion_limit",
25+
"recursion limit");
26+
update_limit(sess, krate, &sess.type_length_limit, "type_length_limit",
27+
"type length limit");
28+
}
29+
30+
fn update_limit(sess: &Session, krate: &ast::Crate, limit: &Cell<usize>,
31+
name: &str, description: &str) {
2232
for attr in &krate.attrs {
23-
if !attr.check_name("recursion_limit") {
33+
if !attr.check_name(name) {
2434
continue;
2535
}
2636

2737
if let Some(s) = attr.value_str() {
2838
if let Some(n) = s.as_str().parse().ok() {
29-
sess.recursion_limit.set(n);
39+
limit.set(n);
3040
return;
3141
}
3242
}
3343

34-
span_err!(sess, attr.span, E0296, "malformed recursion limit attribute, \
35-
expected #![recursion_limit=\"N\"]");
44+
span_err!(sess, attr.span, E0296,
45+
"malformed {} attribute, expected #![{}=\"N\"]",
46+
description, name);
3647
}
3748
}

src/librustc/session/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ pub struct Session {
100100
/// operations such as auto-dereference and monomorphization.
101101
pub recursion_limit: Cell<usize>,
102102

103+
/// The maximum length of types during monomorphization.
104+
pub type_length_limit: Cell<usize>,
105+
103106
/// The metadata::creader module may inject an allocator/panic_runtime
104107
/// dependency if it didn't already find one, and this tracks what was
105108
/// injected.
@@ -620,6 +623,7 @@ pub fn build_session_(sopts: config::Options,
620623
crate_disambiguator: RefCell::new(Symbol::intern("")),
621624
features: RefCell::new(feature_gate::Features::new()),
622625
recursion_limit: Cell::new(64),
626+
type_length_limit: Cell::new(1048576),
623627
next_node_id: Cell::new(NodeId::new(1)),
624628
injected_allocator: Cell::new(None),
625629
injected_panic_runtime: Cell::new(None),

src/librustc_driver/driver.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
566566
*sess.crate_disambiguator.borrow_mut() = Symbol::intern(&compute_crate_disambiguator(sess));
567567

568568
time(time_passes, "recursion limit", || {
569-
middle::recursion_limit::update_recursion_limit(sess, &krate);
569+
middle::recursion_limit::update_limits(sess, &krate);
570570
});
571571

572572
krate = time(time_passes, "crate injection", || {

src/librustc_trans/collector.rs

+35
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(scx: &SharedCrateContext<'a, 'tcx>,
361361
recursion_depth_reset = Some(check_recursion_limit(scx.tcx(),
362362
instance,
363363
recursion_depths));
364+
check_type_length_limit(scx.tcx(), instance);
364365

365366
// Scan the MIR in order to find function calls, closures, and
366367
// drop-glue
@@ -432,6 +433,40 @@ fn check_recursion_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
432433
(instance.def, recursion_depth)
433434
}
434435

436+
fn check_type_length_limit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
437+
instance: Instance<'tcx>)
438+
{
439+
let type_length = instance.substs.types().flat_map(|ty| ty.walk()).count();
440+
debug!(" => type length={}", type_length);
441+
442+
// Rust code can easily create exponentially-long types using only a
443+
// polynomial recursion depth. Even with the default recursion
444+
// depth, you can easily get cases that take >2^60 steps to run,
445+
// which means that rustc basically hangs.
446+
//
447+
// Bail out in these cases to avoid that bad user experience.
448+
let type_length_limit = tcx.sess.type_length_limit.get();
449+
if type_length > type_length_limit {
450+
// The instance name is already known to be too long for rustc. Use
451+
// `{:.64}` to avoid blasting the user's terminal with thousands of
452+
// lines of type-name.
453+
let instance_name = instance.to_string();
454+
let msg = format!("reached the type-length limit while instantiating `{:.64}...`",
455+
instance_name);
456+
let mut diag = if let Some(node_id) = tcx.map.as_local_node_id(instance.def) {
457+
tcx.sess.struct_span_fatal(tcx.map.span(node_id), &msg)
458+
} else {
459+
tcx.sess.struct_fatal(&msg)
460+
};
461+
462+
diag.note(&format!(
463+
"consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate",
464+
type_length_limit*2));
465+
diag.emit();
466+
tcx.sess.abort_if_errors();
467+
}
468+
}
469+
435470
struct MirNeighborCollector<'a, 'tcx: 'a> {
436471
scx: &'a SharedCrateContext<'a, 'tcx>,
437472
mir: &'a mir::Mir<'tcx>,

src/libsyntax/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
738738
("no_main", CrateLevel, Ungated),
739739
("no_builtins", CrateLevel, Ungated),
740740
("recursion_limit", CrateLevel, Ungated),
741+
("type_length_limit", CrateLevel, Ungated),
741742
];
742743

743744
// cfg(...)'s that are feature gated

src/test/compile-fail/issue-22638.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
#![allow(unused)]
1212

13-
#![recursion_limit = "32"]
13+
#![recursion_limit = "20"]
14+
#![type_length_limit = "20000000"]
1415

1516
#[derive(Clone)]
1617
struct A (B);
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern: reached the type-length limit while instantiating
12+
13+
// Test that the type length limit can be changed.
14+
15+
#![allow(dead_code)]
16+
#![type_length_limit="256"]
17+
18+
macro_rules! link {
19+
($id:ident, $t:ty) => {
20+
pub type $id = ($t, $t, $t);
21+
}
22+
}
23+
24+
link! { A, B }
25+
link! { B, C }
26+
link! { C, D }
27+
link! { D, E }
28+
link! { E, F }
29+
link! { F, G }
30+
31+
pub struct G;
32+
33+
fn main() {
34+
drop::<Option<A>>(None);
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait Mirror {
12+
type Image;
13+
}
14+
15+
impl<T> Mirror for T { type Image = T; }
16+
17+
trait Foo {
18+
fn recurse(&self);
19+
}
20+
21+
impl<T> Foo for T {
22+
#[allow(unconditional_recursion)]
23+
fn recurse(&self) {
24+
(self, self).recurse();
25+
}
26+
}
27+
28+
fn main() {
29+
().recurse();
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: reached the type-length limit while instantiating `<T as Foo><(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(), &()), &(&()...`
2+
--> $DIR/issue-37311.rs:23:5
3+
|
4+
23 | fn recurse(&self) {
5+
| _____^ starting here...
6+
24 | | (self, self).recurse();
7+
25 | | }
8+
| |_____^ ...ending here
9+
|
10+
= note: consider adding a `#![type_length_limit="2097152"]` attribute to your crate
11+
12+
error: aborting due to previous error
13+

0 commit comments

Comments
 (0)