Skip to content

Commit 0eea0f6

Browse files
committedJul 25, 2015
Allow writing types which "can't" be instantiated.
The borrow checker doesn't allow constructing such a type at runtime using safe code, but there isn't any reason to ban them in the type checker. Included in this commit is an example of a neat static doubly-linked list. Feature-gated under the static_recursion gate to be on the safe side, but there are unlikely to be any reasons this shouldn't be turned on by default.
1 parent 742e124 commit 0eea0f6

File tree

7 files changed

+72
-46
lines changed

7 files changed

+72
-46
lines changed
 

‎src/librustc_typeck/check/mod.rs

+12-30
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ use syntax::attr::AttrMetaMethods;
115115
use syntax::ast::{self, DefId, Visibility};
116116
use syntax::ast_util::{self, local_def};
117117
use syntax::codemap::{self, Span};
118+
use syntax::feature_gate::emit_feature_err;
118119
use syntax::owned_slice::OwnedSlice;
119120
use syntax::parse::token;
120121
use syntax::print::pprust;
@@ -4009,9 +4010,7 @@ fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
40094010

40104011
/// Checks whether a type can be represented in memory. In particular, it
40114012
/// identifies types that contain themselves without indirection through a
4012-
/// pointer, which would mean their size is unbounded. This is different from
4013-
/// the question of whether a type can be instantiated. See the definition of
4014-
/// `check_instantiable`.
4013+
/// pointer, which would mean their size is unbounded.
40154014
pub fn check_representable(tcx: &ty::ctxt,
40164015
sp: Span,
40174016
item_id: ast::NodeId,
@@ -4036,31 +4035,19 @@ pub fn check_representable(tcx: &ty::ctxt,
40364035
return true
40374036
}
40384037

4039-
/// Checks whether a type can be created without an instance of itself.
4040-
/// This is similar but different from the question of whether a type
4041-
/// can be represented. For example, the following type:
4042-
///
4043-
/// enum foo { None, Some(foo) }
4044-
///
4045-
/// is instantiable but is not representable. Similarly, the type
4046-
///
4047-
/// enum foo { Some(@foo) }
4048-
///
4049-
/// is representable, but not instantiable.
4038+
/// Checks whether a type can be constructed at runtime without
4039+
/// an existing instance of that type.
40504040
pub fn check_instantiable(tcx: &ty::ctxt,
40514041
sp: Span,
4052-
item_id: ast::NodeId)
4053-
-> bool {
4042+
item_id: ast::NodeId) {
40544043
let item_ty = tcx.node_id_to_type(item_id);
4055-
if !item_ty.is_instantiable(tcx) {
4056-
span_err!(tcx.sess, sp, E0073,
4057-
"this type cannot be instantiated without an \
4058-
instance of itself");
4059-
fileline_help!(tcx.sess, sp, "consider using `Option<{:?}>`",
4060-
item_ty);
4061-
false
4062-
} else {
4063-
true
4044+
if !item_ty.is_instantiable(tcx) &&
4045+
!tcx.sess.features.borrow().static_recursion {
4046+
emit_feature_err(&tcx.sess.parse_sess.span_diagnostic,
4047+
"static_recursion",
4048+
sp,
4049+
"this type cannot be instantiated at runtime \
4050+
without an instance of itself");
40644051
}
40654052
}
40664053

@@ -4199,11 +4186,6 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
41994186
do_check(ccx, vs, id, hint);
42004187

42014188
check_representable(ccx.tcx, sp, id, "enum");
4202-
4203-
// Check that it is possible to instantiate this enum:
4204-
//
4205-
// This *sounds* like the same that as representable, but it's
4206-
// not. See def'n of `check_instantiable()` for details.
42074189
check_instantiable(ccx.tcx, sp, id);
42084190
}
42094191

‎src/test/compile-fail/issue-3008-2.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(static_recursion)]
12+
1113
enum foo { foo_(bar) }
1214
struct bar { x: bar }
1315
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
14-
//~^^ ERROR this type cannot be instantiated without an instance of itself
1516

1617
fn main() {
1718
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
struct Z(&'static Z);
12+
//~^ ERROR this type cannot be instantiated
13+
14+
pub fn main() {}

‎src/test/compile-fail/type-recursive.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// error-pattern:this type cannot be instantiated
11+
// error-pattern:illegal recursive struct type
1212
struct t1 {
1313
foo: isize,
1414
foolish: t1

‎src/test/compile-fail/issue-2063-resource.rs ‎src/test/run-pass/issue-2063-resource.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(static_recursion)]
1112

1213
// test that autoderef of a type like this does not
1314
// cause compiler to loop. Note that no instances
1415
// of such a type could ever be constructed.
15-
struct S { //~ ERROR this type cannot be instantiated
16+
17+
struct S {
1618
x: X,
1719
to_str: (),
1820
}
1921

20-
struct X(Box<S>); //~ ERROR this type cannot be instantiated
22+
struct X(Box<S>);
2123

2224
fn main() {}

‎src/test/compile-fail/issue-2063.rs ‎src/test/run-pass/issue-2063.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,25 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(static_recursion)]
12+
1113
// test that autoderef of a type like this does not
1214
// cause compiler to loop. Note that no instances
1315
// of such a type could ever be constructed.
1416

15-
struct t(Box<t>); //~ ERROR this type cannot be instantiated
17+
struct T(Box<T>);
1618

17-
trait to_str_2 {
18-
fn my_to_string() -> String;
19+
trait ToStr2 {
20+
fn my_to_string(&self) -> String;
1921
}
2022

21-
// I use an impl here because it will cause
22-
// the compiler to attempt autoderef and then
23-
// try to resolve the method.
24-
impl to_str_2 for t {
25-
fn my_to_string() -> String { "t".to_string() }
23+
impl ToStr2 for T {
24+
fn my_to_string(&self) -> String { "t".to_string() }
2625
}
2726

28-
fn new_t(x: t) {
27+
#[allow(dead_code)]
28+
fn new_t(x: T) {
2929
x.my_to_string();
30-
// (there used to be an error emitted right here as well. It was
31-
// spurious, at best; if `t` did exist as a type, it clearly would
32-
// have an impl of the `to_str_2` trait.)
3330
}
3431

3532
fn main() {

‎src/test/run-pass/static-recursive.rs

+30
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,36 @@
1212

1313
static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 };
1414

15+
struct StaticDoubleLinked {
16+
prev: &'static StaticDoubleLinked,
17+
next: &'static StaticDoubleLinked,
18+
data: i32,
19+
head: bool
20+
}
21+
22+
static L1: StaticDoubleLinked = StaticDoubleLinked{prev: &L3, next: &L2, data: 1, head: true};
23+
static L2: StaticDoubleLinked = StaticDoubleLinked{prev: &L1, next: &L3, data: 2, head: false};
24+
static L3: StaticDoubleLinked = StaticDoubleLinked{prev: &L2, next: &L1, data: 3, head: false};
25+
26+
1527
pub fn main() {
1628
unsafe { assert_eq!(S, *(S as *const *const u8)); }
29+
30+
let mut test_vec = Vec::new();
31+
let mut cur = &L1;
32+
loop {
33+
test_vec.push(cur.data);
34+
cur = cur.next;
35+
if cur.head { break }
36+
}
37+
assert_eq!(&test_vec, &[1,2,3]);
38+
39+
let mut test_vec = Vec::new();
40+
let mut cur = &L1;
41+
loop {
42+
cur = cur.prev;
43+
test_vec.push(cur.data);
44+
if cur.head { break }
45+
}
46+
assert_eq!(&test_vec, &[3,2,1]);
1747
}

0 commit comments

Comments
 (0)