Skip to content

Commit 85bdb31

Browse files
committed
Rollup merge of rust-lang#22777 - pnkfelix:issue-22443, r=nikomatsakis
Check for unbounded recursion during dropck. Such recursion can be introduced by the erroneous use of non-regular types (aka types employing polymorphic recursion), which Rust does not support. Fix rust-lang#22443
2 parents fb19cd7 + 180ef47 commit 85bdb31

File tree

6 files changed

+299
-24
lines changed

6 files changed

+299
-24
lines changed

src/librustc_typeck/check/dropck.rs

+123-23
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use middle::infer;
1414
use middle::region;
1515
use middle::subst;
1616
use middle::ty::{self, Ty};
17-
use util::ppaux::{Repr};
17+
use util::ppaux::{Repr, UserString};
1818

19+
use syntax::ast;
1920
use syntax::codemap::Span;
2021

2122
pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
@@ -28,29 +29,98 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
2829
// types that have been traversed so far by `traverse_type_if_unseen`
2930
let mut breadcrumbs: Vec<Ty<'tcx>> = Vec::new();
3031

31-
iterate_over_potentially_unsafe_regions_in_type(
32+
let result = iterate_over_potentially_unsafe_regions_in_type(
3233
rcx,
3334
&mut breadcrumbs,
35+
TypeContext::Root,
3436
typ,
3537
span,
3638
scope,
39+
0,
3740
0);
41+
match result {
42+
Ok(()) => {}
43+
Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => {
44+
let tcx = rcx.tcx();
45+
span_err!(tcx.sess, span, E0320,
46+
"overflow while adding drop-check rules for {}",
47+
typ.user_string(rcx.tcx()));
48+
match *ctxt {
49+
TypeContext::Root => {
50+
// no need for an additional note if the overflow
51+
// was somehow on the root.
52+
}
53+
TypeContext::EnumVariant { def_id, variant, arg_index } => {
54+
// FIXME (pnkfelix): eventually lookup arg_name
55+
// for the given index on struct variants.
56+
span_note!(
57+
rcx.tcx().sess,
58+
span,
59+
"overflowed on enum {} variant {} argument {} type: {}",
60+
ty::item_path_str(tcx, def_id),
61+
variant,
62+
arg_index,
63+
detected_on_typ.user_string(rcx.tcx()));
64+
}
65+
TypeContext::Struct { def_id, field } => {
66+
span_note!(
67+
rcx.tcx().sess,
68+
span,
69+
"overflowed on struct {} field {} type: {}",
70+
ty::item_path_str(tcx, def_id),
71+
field,
72+
detected_on_typ.user_string(rcx.tcx()));
73+
}
74+
}
75+
}
76+
}
77+
}
78+
79+
enum Error<'tcx> {
80+
Overflow(TypeContext, ty::Ty<'tcx>),
81+
}
82+
83+
enum TypeContext {
84+
Root,
85+
EnumVariant {
86+
def_id: ast::DefId,
87+
variant: ast::Name,
88+
arg_index: usize,
89+
},
90+
Struct {
91+
def_id: ast::DefId,
92+
field: ast::Name,
93+
}
3894
}
3995

96+
// The `depth` counts the number of calls to this function;
97+
// the `xref_depth` counts the subset of such calls that go
98+
// across a `Box<T>` or `PhantomData<T>`.
4099
fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
41100
rcx: &mut Rcx<'a, 'tcx>,
42101
breadcrumbs: &mut Vec<Ty<'tcx>>,
102+
context: TypeContext,
43103
ty_root: ty::Ty<'tcx>,
44104
span: Span,
45105
scope: region::CodeExtent,
46-
depth: uint)
106+
depth: uint,
107+
xref_depth: uint) -> Result<(), Error<'tcx>>
47108
{
109+
// Issue #22443: Watch out for overflow. While we are careful to
110+
// handle regular types properly, non-regular ones cause problems.
111+
let recursion_limit = rcx.tcx().sess.recursion_limit.get();
112+
if xref_depth >= recursion_limit {
113+
return Err(Error::Overflow(context, ty_root))
114+
}
115+
48116
let origin = || infer::SubregionOrigin::SafeDestructor(span);
49117
let mut walker = ty_root.walk();
50118
let opt_phantom_data_def_id = rcx.tcx().lang_items.phantom_data();
51119

52120
let destructor_for_type = rcx.tcx().destructor_for_type.borrow();
53121

122+
let xref_depth_orig = xref_depth;
123+
54124
while let Some(typ) = walker.next() {
55125
// Avoid recursing forever.
56126
if breadcrumbs.contains(&typ) {
@@ -61,20 +131,33 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
61131
// If we encounter `PhantomData<T>`, then we should replace it
62132
// with `T`, the type it represents as owned by the
63133
// surrounding context, before doing further analysis.
64-
let typ = if let ty::ty_struct(struct_did, substs) = typ.sty {
65-
if opt_phantom_data_def_id == Some(struct_did) {
66-
let item_type = ty::lookup_item_type(rcx.tcx(), struct_did);
67-
let tp_def = item_type.generics.types
68-
.opt_get(subst::TypeSpace, 0).unwrap();
69-
let new_typ = substs.type_for_def(tp_def);
70-
debug!("replacing phantom {} with {}",
134+
let (typ, xref_depth) = match typ.sty {
135+
ty::ty_struct(struct_did, substs) => {
136+
if opt_phantom_data_def_id == Some(struct_did) {
137+
let item_type = ty::lookup_item_type(rcx.tcx(), struct_did);
138+
let tp_def = item_type.generics.types
139+
.opt_get(subst::TypeSpace, 0).unwrap();
140+
let new_typ = substs.type_for_def(tp_def);
141+
debug!("replacing phantom {} with {}",
142+
typ.repr(rcx.tcx()), new_typ.repr(rcx.tcx()));
143+
(new_typ, xref_depth_orig + 1)
144+
} else {
145+
(typ, xref_depth_orig)
146+
}
147+
}
148+
149+
// Note: When ty_uniq is removed from compiler, the
150+
// definition of `Box<T>` must carry a PhantomData that
151+
// puts us into the previous case.
152+
ty::ty_uniq(new_typ) => {
153+
debug!("replacing ty_uniq {} with {}",
71154
typ.repr(rcx.tcx()), new_typ.repr(rcx.tcx()));
72-
new_typ
73-
} else {
74-
typ
155+
(new_typ, xref_depth_orig + 1)
156+
}
157+
158+
_ => {
159+
(typ, xref_depth_orig)
75160
}
76-
} else {
77-
typ
78161
};
79162

80163
let opt_type_did = match typ.sty {
@@ -87,9 +170,9 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
87170
opt_type_did.and_then(|did| destructor_for_type.get(&did));
88171

89172
debug!("iterate_over_potentially_unsafe_regions_in_type \
90-
{}typ: {} scope: {:?} opt_dtor: {:?}",
173+
{}typ: {} scope: {:?} opt_dtor: {:?} xref: {}",
91174
(0..depth).map(|_| ' ').collect::<String>(),
92-
typ.repr(rcx.tcx()), scope, opt_dtor);
175+
typ.repr(rcx.tcx()), scope, opt_dtor, xref_depth);
93176

94177
// If `typ` has a destructor, then we must ensure that all
95178
// borrowed data reachable via `typ` must outlive the parent
@@ -228,6 +311,8 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
228311

229312
match typ.sty {
230313
ty::ty_struct(struct_did, substs) => {
314+
debug!("typ: {} is struct; traverse structure and not type-expression",
315+
typ.repr(rcx.tcx()));
231316
// Don't recurse; we extract type's substructure,
232317
// so do not process subparts of type expression.
233318
walker.skip_current_subtree();
@@ -240,17 +325,24 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
240325
struct_did,
241326
field.id,
242327
substs);
243-
iterate_over_potentially_unsafe_regions_in_type(
328+
try!(iterate_over_potentially_unsafe_regions_in_type(
244329
rcx,
245330
breadcrumbs,
331+
TypeContext::Struct {
332+
def_id: struct_did,
333+
field: field.name,
334+
},
246335
field_type,
247336
span,
248337
scope,
249-
depth+1)
338+
depth+1,
339+
xref_depth))
250340
}
251341
}
252342

253343
ty::ty_enum(enum_did, substs) => {
344+
debug!("typ: {} is enum; traverse structure and not type-expression",
345+
typ.repr(rcx.tcx()));
254346
// Don't recurse; we extract type's substructure,
255347
// so do not process subparts of type expression.
256348
walker.skip_current_subtree();
@@ -260,14 +352,20 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
260352
enum_did,
261353
substs);
262354
for variant_info in all_variant_info.iter() {
263-
for argument_type in variant_info.args.iter() {
264-
iterate_over_potentially_unsafe_regions_in_type(
355+
for (i, arg_type) in variant_info.args.iter().enumerate() {
356+
try!(iterate_over_potentially_unsafe_regions_in_type(
265357
rcx,
266358
breadcrumbs,
267-
*argument_type,
359+
TypeContext::EnumVariant {
360+
def_id: enum_did,
361+
variant: variant_info.name,
362+
arg_index: i,
363+
},
364+
*arg_type,
268365
span,
269366
scope,
270-
depth+1)
367+
depth+1,
368+
xref_depth));
271369
}
272370
}
273371
}
@@ -290,4 +388,6 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
290388
// is done.
291389
}
292390
}
391+
392+
return Ok(());
293393
}

src/librustc_typeck/diagnostics.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ register_diagnostics! {
174174
E0249, // expected constant expr for array length
175175
E0250, // expected constant expr for array length
176176
E0318, // can't create default impls for traits outside their crates
177-
E0319 // trait impls for defaulted traits allowed just for structs/enums
177+
E0319, // trait impls for defaulted traits allowed just for structs/enums
178+
E0320 // recursive overflow during dropck
178179
}
179180

180181
__build_diagnostic_array! { DIAGNOSTICS }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2015 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+
// Issue 22443: Reject code using non-regular types that would
12+
// otherwise cause dropck to loop infinitely.
13+
14+
use std::marker::PhantomData;
15+
16+
struct Digit<T> {
17+
elem: T
18+
}
19+
20+
struct Node<T:'static> { m: PhantomData<&'static T> }
21+
22+
23+
enum FingerTree<T:'static> {
24+
Single(T),
25+
// Bug report said Digit after Box would stack overflow (versus
26+
// Digit before Box; see dropck_no_diverge_on_nonregular_2).
27+
Deep(
28+
Box<FingerTree<Node<T>>>,
29+
Digit<T>,
30+
)
31+
}
32+
33+
fn main() {
34+
let ft = //~ ERROR overflow while adding drop-check rules for FingerTree
35+
FingerTree::Single(1);
36+
//~^ ERROR overflow while adding drop-check rules for FingerTree
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2015 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+
// Issue 22443: Reject code using non-regular types that would
12+
// otherwise cause dropck to loop infinitely.
13+
14+
use std::marker::PhantomData;
15+
16+
struct Digit<T> {
17+
elem: T
18+
}
19+
20+
struct Node<T:'static> { m: PhantomData<&'static T> }
21+
22+
enum FingerTree<T:'static> {
23+
Single(T),
24+
// Bug report said Digit before Box would infinite loop (versus
25+
// Digit after Box; see dropck_no_diverge_on_nonregular_1).
26+
Deep(
27+
Digit<T>,
28+
Box<FingerTree<Node<T>>>,
29+
)
30+
}
31+
32+
fn main() {
33+
let ft = //~ ERROR overflow while adding drop-check rules for FingerTree
34+
FingerTree::Single(1);
35+
//~^ ERROR overflow while adding drop-check rules for FingerTree
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2015 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+
// Issue 22443: Reject code using non-regular types that would
12+
// otherwise cause dropck to loop infinitely.
13+
//
14+
// This version is just checking that we still sanely handle a trivial
15+
// wrapper around the non-regular type. (It also demonstrates how the
16+
// error messages will report different types depending on which type
17+
// dropck is analyzing.)
18+
19+
use std::marker::PhantomData;
20+
21+
struct Digit<T> {
22+
elem: T
23+
}
24+
25+
struct Node<T:'static> { m: PhantomData<&'static T> }
26+
27+
enum FingerTree<T:'static> {
28+
Single(T),
29+
// According to the bug report, Digit before Box would infinite loop.
30+
Deep(
31+
Digit<T>,
32+
Box<FingerTree<Node<T>>>,
33+
)
34+
}
35+
36+
enum Wrapper<T:'static> {
37+
Simple,
38+
Other(FingerTree<T>),
39+
}
40+
41+
fn main() {
42+
let w = //~ ERROR overflow while adding drop-check rules for core::option
43+
Some(Wrapper::Simple::<u32>);
44+
//~^ ERROR overflow while adding drop-check rules for core::option::Option
45+
//~| ERROR overflow while adding drop-check rules for Wrapper
46+
}

0 commit comments

Comments
 (0)