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.
This commit is contained in:
parent
742e1242d9
commit
0eea0f6e90
|
@ -115,6 +115,7 @@ use syntax::attr::AttrMetaMethods;
|
|||
use syntax::ast::{self, DefId, Visibility};
|
||||
use syntax::ast_util::{self, local_def};
|
||||
use syntax::codemap::{self, Span};
|
||||
use syntax::feature_gate::emit_feature_err;
|
||||
use syntax::owned_slice::OwnedSlice;
|
||||
use syntax::parse::token;
|
||||
use syntax::print::pprust;
|
||||
|
@ -4009,9 +4010,7 @@ fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||
|
||||
/// Checks whether a type can be represented in memory. In particular, it
|
||||
/// identifies types that contain themselves without indirection through a
|
||||
/// pointer, which would mean their size is unbounded. This is different from
|
||||
/// the question of whether a type can be instantiated. See the definition of
|
||||
/// `check_instantiable`.
|
||||
/// pointer, which would mean their size is unbounded.
|
||||
pub fn check_representable(tcx: &ty::ctxt,
|
||||
sp: Span,
|
||||
item_id: ast::NodeId,
|
||||
|
@ -4036,31 +4035,19 @@ pub fn check_representable(tcx: &ty::ctxt,
|
|||
return true
|
||||
}
|
||||
|
||||
/// Checks whether a type can be created without an instance of itself.
|
||||
/// This is similar but different from the question of whether a type
|
||||
/// can be represented. For example, the following type:
|
||||
///
|
||||
/// enum foo { None, Some(foo) }
|
||||
///
|
||||
/// is instantiable but is not representable. Similarly, the type
|
||||
///
|
||||
/// enum foo { Some(@foo) }
|
||||
///
|
||||
/// is representable, but not instantiable.
|
||||
/// Checks whether a type can be constructed at runtime without
|
||||
/// an existing instance of that type.
|
||||
pub fn check_instantiable(tcx: &ty::ctxt,
|
||||
sp: Span,
|
||||
item_id: ast::NodeId)
|
||||
-> bool {
|
||||
item_id: ast::NodeId) {
|
||||
let item_ty = tcx.node_id_to_type(item_id);
|
||||
if !item_ty.is_instantiable(tcx) {
|
||||
span_err!(tcx.sess, sp, E0073,
|
||||
"this type cannot be instantiated without an \
|
||||
instance of itself");
|
||||
fileline_help!(tcx.sess, sp, "consider using `Option<{:?}>`",
|
||||
item_ty);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
if !item_ty.is_instantiable(tcx) &&
|
||||
!tcx.sess.features.borrow().static_recursion {
|
||||
emit_feature_err(&tcx.sess.parse_sess.span_diagnostic,
|
||||
"static_recursion",
|
||||
sp,
|
||||
"this type cannot be instantiated at runtime \
|
||||
without an instance of itself");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4199,11 +4186,6 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
|||
do_check(ccx, vs, id, hint);
|
||||
|
||||
check_representable(ccx.tcx, sp, id, "enum");
|
||||
|
||||
// Check that it is possible to instantiate this enum:
|
||||
//
|
||||
// This *sounds* like the same that as representable, but it's
|
||||
// not. See def'n of `check_instantiable()` for details.
|
||||
check_instantiable(ccx.tcx, sp, id);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(static_recursion)]
|
||||
|
||||
enum foo { foo_(bar) }
|
||||
struct bar { x: bar }
|
||||
//~^ ERROR illegal recursive struct type; wrap the inner value in a box to make it representable
|
||||
//~^^ ERROR this type cannot be instantiated without an instance of itself
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
struct Z(&'static Z);
|
||||
//~^ ERROR this type cannot be instantiated
|
||||
|
||||
pub fn main() {}
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern:this type cannot be instantiated
|
||||
// error-pattern:illegal recursive struct type
|
||||
struct t1 {
|
||||
foo: isize,
|
||||
foolish: t1
|
||||
|
|
|
@ -8,15 +8,17 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(static_recursion)]
|
||||
|
||||
// test that autoderef of a type like this does not
|
||||
// cause compiler to loop. Note that no instances
|
||||
// of such a type could ever be constructed.
|
||||
struct S { //~ ERROR this type cannot be instantiated
|
||||
|
||||
struct S {
|
||||
x: X,
|
||||
to_str: (),
|
||||
}
|
||||
|
||||
struct X(Box<S>); //~ ERROR this type cannot be instantiated
|
||||
struct X(Box<S>);
|
||||
|
||||
fn main() {}
|
|
@ -8,28 +8,25 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(static_recursion)]
|
||||
|
||||
// test that autoderef of a type like this does not
|
||||
// cause compiler to loop. Note that no instances
|
||||
// of such a type could ever be constructed.
|
||||
|
||||
struct t(Box<t>); //~ ERROR this type cannot be instantiated
|
||||
struct T(Box<T>);
|
||||
|
||||
trait to_str_2 {
|
||||
fn my_to_string() -> String;
|
||||
trait ToStr2 {
|
||||
fn my_to_string(&self) -> String;
|
||||
}
|
||||
|
||||
// I use an impl here because it will cause
|
||||
// the compiler to attempt autoderef and then
|
||||
// try to resolve the method.
|
||||
impl to_str_2 for t {
|
||||
fn my_to_string() -> String { "t".to_string() }
|
||||
impl ToStr2 for T {
|
||||
fn my_to_string(&self) -> String { "t".to_string() }
|
||||
}
|
||||
|
||||
fn new_t(x: t) {
|
||||
#[allow(dead_code)]
|
||||
fn new_t(x: T) {
|
||||
x.my_to_string();
|
||||
// (there used to be an error emitted right here as well. It was
|
||||
// spurious, at best; if `t` did exist as a type, it clearly would
|
||||
// have an impl of the `to_str_2` trait.)
|
||||
}
|
||||
|
||||
fn main() {
|
|
@ -12,6 +12,36 @@
|
|||
|
||||
static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 };
|
||||
|
||||
struct StaticDoubleLinked {
|
||||
prev: &'static StaticDoubleLinked,
|
||||
next: &'static StaticDoubleLinked,
|
||||
data: i32,
|
||||
head: bool
|
||||
}
|
||||
|
||||
static L1: StaticDoubleLinked = StaticDoubleLinked{prev: &L3, next: &L2, data: 1, head: true};
|
||||
static L2: StaticDoubleLinked = StaticDoubleLinked{prev: &L1, next: &L3, data: 2, head: false};
|
||||
static L3: StaticDoubleLinked = StaticDoubleLinked{prev: &L2, next: &L1, data: 3, head: false};
|
||||
|
||||
|
||||
pub fn main() {
|
||||
unsafe { assert_eq!(S, *(S as *const *const u8)); }
|
||||
|
||||
let mut test_vec = Vec::new();
|
||||
let mut cur = &L1;
|
||||
loop {
|
||||
test_vec.push(cur.data);
|
||||
cur = cur.next;
|
||||
if cur.head { break }
|
||||
}
|
||||
assert_eq!(&test_vec, &[1,2,3]);
|
||||
|
||||
let mut test_vec = Vec::new();
|
||||
let mut cur = &L1;
|
||||
loop {
|
||||
cur = cur.prev;
|
||||
test_vec.push(cur.data);
|
||||
if cur.head { break }
|
||||
}
|
||||
assert_eq!(&test_vec, &[3,2,1]);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue