Skip to content

Commit

Permalink
Introduce a common recursion limit for auto-dereference and monomorph…
Browse files Browse the repository at this point in the history
…ization.
  • Loading branch information
eddyb committed Mar 13, 2014
1 parent 20b4e15 commit 26398b4
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 58 deletions.
3 changes: 2 additions & 1 deletion src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,8 @@ pub fn build_session_(sopts: @session::Options,
lints: RefCell::new(HashMap::new()),
node_id: Cell::new(1),
crate_types: @RefCell::new(Vec::new()),
features: front::feature_gate::Features::new()
features: front::feature_gate::Features::new(),
recursion_limit: Cell::new(64),
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/librustc/driver/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@ pub struct Session_ {
Vec<(lint::Lint, codemap::Span, ~str)> >>,
node_id: Cell<ast::NodeId>,
crate_types: @RefCell<Vec<CrateType> >,
features: front::feature_gate::Features
features: front::feature_gate::Features,

/// The maximum recursion limit for potentially infinitely recursive
/// operations such as auto-dereference and monomorphization.
recursion_limit: Cell<uint>,
}

pub type Session = @Session_;
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/middle/trans/monomorphize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,11 @@ pub fn monomorphic_fn(ccx: @CrateContext,
// Random cut-off -- code that needs to instantiate the same function
// recursively more than thirty times can probably safely be assumed
// to be causing an infinite expansion.
if depth > 30 {
ccx.sess.span_fatal(
ccx.tcx.map.span(fn_id.node),
"overly deep expansion of inlined function");
if depth > ccx.sess.recursion_limit.get() {
ccx.sess.span_fatal(ccx.tcx.map.span(fn_id.node),
"reached the recursion limit during monomorphization");
}

monomorphizing.get().insert(fn_id, depth + 1);
}

Expand Down
31 changes: 0 additions & 31 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3467,37 +3467,6 @@ pub fn param_tys_in_type(ty: t) -> Vec<param_ty> {
rslt
}

pub fn occurs_check(tcx: ctxt, sp: Span, vid: TyVid, rt: t) {
// Returns a vec of all the type variables occurring in `ty`. It may
// contain duplicates. (Integral type vars aren't counted.)
fn vars_in_type(ty: t) -> Vec<TyVid> {
let mut rslt = Vec::new();
walk_ty(ty, |ty| {
match get(ty).sty {
ty_infer(TyVar(v)) => rslt.push(v),
_ => ()
}
});
rslt
}

// Fast path
if !type_needs_infer(rt) { return; }

// Occurs check!
if vars_in_type(rt).contains(&vid) {
// Maybe this should be span_err -- however, there's an
// assertion later on that the type doesn't contain
// variables, so in this case we have to be sure to die.
tcx.sess.span_fatal
(sp, ~"type inference failed because I \
could not find a type\n that's both of the form "
+ ::util::ppaux::ty_to_str(tcx, mk_var(tcx, vid)) +
" and of the form " + ::util::ppaux::ty_to_str(tcx, rt) +
" - such a type would have to be infinitely large.");
}
}

pub fn ty_sort_str(cx: ctxt, t: t) -> ~str {
match get(t).sty {
ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) |
Expand Down
27 changes: 9 additions & 18 deletions src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ pub enum LvaluePreference {
NoPreference
}

pub fn autoderef<T>(fcx: @FnCtxt, sp: Span, t: ty::t,
pub fn autoderef<T>(fcx: @FnCtxt, sp: Span, base_ty: ty::t,
expr_id: Option<ast::NodeId>,
mut lvalue_pref: LvaluePreference,
should_stop: |ty::t, uint| -> Option<T>)
Expand All @@ -1253,24 +1253,10 @@ pub fn autoderef<T>(fcx: @FnCtxt, sp: Span, t: ty::t,
* responsible for inserting an AutoAdjustment record into `tcx.adjustments`
* so that trans/borrowck/etc know about this autoderef. */

let mut t = t;
let mut autoderefs = 0;
loop {
let mut t = base_ty;
for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
let resolved_t = structurally_resolved_type(fcx, sp, t);

// Some extra checks to detect weird cycles and so forth:
match ty::get(resolved_t).sty {
ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_rptr(_, _) => {
match ty::get(t).sty {
ty::ty_infer(ty::TyVar(v1)) => {
ty::occurs_check(fcx.ccx.tcx, sp, v1, resolved_t);
}
_ => {}
}
}
_ => { /*ok*/ }
}

match should_stop(resolved_t, autoderefs) {
Some(x) => return (resolved_t, autoderefs, Some(x)),
None => {}
Expand All @@ -1291,11 +1277,16 @@ pub fn autoderef<T>(fcx: @FnCtxt, sp: Span, t: ty::t,
if mt.mutbl == ast::MutImmutable {
lvalue_pref = NoPreference;
}
autoderefs += 1;
}
None => return (resolved_t, autoderefs, None)
}
}

// We've reached the recursion limit, error gracefully.
fcx.tcx().sess.span_err(sp,
format!("reached the recursion limit while auto-dereferencing {}",
base_ty.repr(fcx.tcx())));
(ty::mk_err(), 0, None)
}

fn try_overloaded_deref(fcx: @FnCtxt,
Expand Down
33 changes: 33 additions & 0 deletions src/test/compile-fail/infinite-autoderef.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2014 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 recursion limit while auto-dereferencing

use std::ops::Deref;

struct Foo;

impl Deref<Foo> for Foo {
fn deref<'a>(&'a self) -> &'a Foo {
self
}
}

pub fn main() {
let mut x;
loop {
x = ~x;
x.foo;
x.bar();
}

Foo.foo;
Foo.bar();
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/infinite-instantiation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern: overly deep expansion
// error-pattern: reached the recursion limit during monomorphization
// issue 2258

trait to_opt {
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-8727.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
struct Data(~Option<Data>);

fn generic<T>( _ : ~[(Data,T)] ) {
//~^ ERROR overly deep expansion of inlined function
//~^ ERROR reached the recursion limit during monomorphization
let rec : ~[(Data,(bool,T))] = ~[];
generic( rec );
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/recursion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl<T:Dot> Dot for Cons<T> {
}
}
fn test<T:Dot> (n:int, i:int, first:T, second:T) ->int {
//~^ ERROR: overly deep expansion of inlined function
//~^ ERROR: reached the recursion limit during monomorphization
match n {
0 => {first.dot(second)}
// Error message should be here. It should be a type error
Expand Down

0 comments on commit 26398b4

Please sign in to comment.