Detect and report types which could never be instantiated.
Fixes #2063.
This commit is contained in:
parent
8cf44bed57
commit
23f92ea370
@ -15,6 +15,7 @@ import util::ppaux::ty_to_str;
|
||||
import util::ppaux::ty_constr_to_str;
|
||||
import syntax::print::pprust::*;
|
||||
|
||||
export is_instantiable;
|
||||
export node_id_to_type;
|
||||
export node_id_to_type_params;
|
||||
export arg;
|
||||
@ -1007,6 +1008,136 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
|
||||
ret result;
|
||||
}
|
||||
|
||||
// True if instantiating an instance of `ty` requires an instead of `r_ty`.
|
||||
fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
|
||||
|
||||
fn type_requires(cx: ctxt, seen: @mut [def_id],
|
||||
r_ty: t, ty: t) -> bool {
|
||||
#debug["type_requires(%s, %s)?",
|
||||
ty_to_str(cx, r_ty),
|
||||
ty_to_str(cx, ty)];
|
||||
|
||||
let r = {
|
||||
get(r_ty).struct == get(ty).struct ||
|
||||
subtypes_require(cx, seen, r_ty, ty)
|
||||
};
|
||||
|
||||
#debug["type_requires(%s, %s)? %b",
|
||||
ty_to_str(cx, r_ty),
|
||||
ty_to_str(cx, ty),
|
||||
r];
|
||||
ret r;
|
||||
}
|
||||
|
||||
fn subtypes_require(cx: ctxt, seen: @mut [def_id],
|
||||
r_ty: t, ty: t) -> bool {
|
||||
#debug["subtypes_require(%s, %s)?",
|
||||
ty_to_str(cx, r_ty),
|
||||
ty_to_str(cx, ty)];
|
||||
|
||||
let r = alt get(ty).struct {
|
||||
ty_nil |
|
||||
ty_bot |
|
||||
ty_bool |
|
||||
ty_int(_) |
|
||||
ty_uint(_) |
|
||||
ty_float(_) |
|
||||
ty_str |
|
||||
ty_fn(_) |
|
||||
ty_var(_) |
|
||||
ty_param(_, _) |
|
||||
ty_self(_) |
|
||||
ty_type |
|
||||
ty_opaque_box |
|
||||
ty_opaque_closure_ptr(_) |
|
||||
ty_vec(_) {
|
||||
false
|
||||
}
|
||||
|
||||
ty_constr(t, _) {
|
||||
type_requires(cx, seen, r_ty, t)
|
||||
}
|
||||
|
||||
ty_box(mt) |
|
||||
ty_uniq(mt) |
|
||||
ty_ptr(mt) |
|
||||
ty_rptr(_, mt) {
|
||||
be type_requires(cx, seen, r_ty, mt.ty);
|
||||
}
|
||||
|
||||
ty_rec(fields) {
|
||||
vec::any(fields) {|field|
|
||||
type_requires(cx, seen, r_ty, field.mt.ty)
|
||||
}
|
||||
}
|
||||
|
||||
ty_iface(_, _) {
|
||||
false
|
||||
}
|
||||
|
||||
ty_class(did, _) if vec::contains(*seen, did) {
|
||||
false
|
||||
}
|
||||
|
||||
ty_class(did, tps) {
|
||||
vec::push(*seen, did);
|
||||
let r = vec::any(lookup_class_fields(cx, did)) {|f|
|
||||
let fty = ty::lookup_item_type(cx, f.id);
|
||||
let sty = substitute_type_params(cx, tps, fty.ty);
|
||||
type_requires(cx, seen, r_ty, sty)
|
||||
};
|
||||
vec::pop(*seen);
|
||||
r
|
||||
}
|
||||
|
||||
ty_res(did, _, _) if vec::contains(*seen, did) {
|
||||
false
|
||||
}
|
||||
|
||||
ty_res(did, sub, tps) {
|
||||
vec::push(*seen, did);
|
||||
let sty = substitute_type_params(cx, tps, sub);
|
||||
let r = type_requires(cx, seen, r_ty, sty);
|
||||
vec::pop(*seen);
|
||||
r
|
||||
}
|
||||
|
||||
ty_tup(ts) {
|
||||
vec::any(ts) {|t|
|
||||
type_requires(cx, seen, r_ty, t)
|
||||
}
|
||||
}
|
||||
|
||||
ty_enum(did, _) if vec::contains(*seen, did) {
|
||||
false
|
||||
}
|
||||
|
||||
ty_enum(did, tps) {
|
||||
vec::push(*seen, did);
|
||||
let vs = enum_variants(cx, did);
|
||||
let r = vec::len(*vs) > 0u && vec::all(*vs) {|variant|
|
||||
vec::any(variant.args) {|aty|
|
||||
let sty = substitute_type_params(cx, tps, aty);
|
||||
type_requires(cx, seen, r_ty, sty)
|
||||
}
|
||||
};
|
||||
vec::pop(*seen);
|
||||
r
|
||||
}
|
||||
};
|
||||
|
||||
#debug["subtypes_require(%s, %s)? %b",
|
||||
ty_to_str(cx, r_ty),
|
||||
ty_to_str(cx, ty),
|
||||
r];
|
||||
|
||||
ret r;
|
||||
}
|
||||
|
||||
let seen = @mut [];
|
||||
!subtypes_require(cx, seen, r_ty, r_ty)
|
||||
}
|
||||
|
||||
fn type_structurally_contains(cx: ctxt, ty: t, test: fn(sty) -> bool) ->
|
||||
bool {
|
||||
let sty = get(ty).struct;
|
||||
|
@ -1145,6 +1145,7 @@ mod unify {
|
||||
// instead of ty::struct.
|
||||
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
|
||||
let mut t1 = t;
|
||||
let mut enum_dids = [];
|
||||
loop {
|
||||
alt structure_of(fcx, sp, t1) {
|
||||
ty::ty_box(inner) | ty::ty_uniq(inner) | ty::ty_rptr(_, inner) {
|
||||
@ -1161,6 +1162,16 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
|
||||
t1 = ty::substitute_type_params(fcx.ccx.tcx, tps, inner);
|
||||
}
|
||||
ty::ty_enum(did, tps) {
|
||||
// Watch out for a type like `enum t = @t`. Such a type would
|
||||
// otherwise infinitely auto-deref. This is the only autoderef
|
||||
// loop that needs to be concerned with this, as an error will be
|
||||
// reported on the enum definition as well because the enum is not
|
||||
// instantiable.
|
||||
if vec::contains(enum_dids, did) {
|
||||
ret t1;
|
||||
}
|
||||
vec::push(enum_dids, did);
|
||||
|
||||
let variants = ty::enum_variants(fcx.ccx.tcx, did);
|
||||
if vec::len(*variants) != 1u || vec::len(variants[0].args) != 1u {
|
||||
ret t1;
|
||||
@ -3396,6 +3407,18 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
|
||||
demand::simple(fcx, e.span, declty, cty);
|
||||
}
|
||||
|
||||
fn check_instantiable(tcx: ty::ctxt,
|
||||
sp: span,
|
||||
item_id: ast::node_id) {
|
||||
let rty = ty::node_id_to_type(tcx, item_id);
|
||||
if !ty::is_instantiable(tcx, rty) {
|
||||
tcx.sess.span_err(sp, #fmt["this type cannot be instantiated \
|
||||
without an instance of itself. \
|
||||
Consider using option<%s>.",
|
||||
ty_to_str(tcx, rty)]);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
|
||||
id: ast::node_id) {
|
||||
// FIXME: this is kinda a kludge; we manufacture a fake function context
|
||||
@ -3443,6 +3466,8 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
|
||||
disr_vals += [disr_val];
|
||||
disr_val += 1;
|
||||
}
|
||||
|
||||
// Check that it is possible to represent this enum:
|
||||
let mut outer = true, did = local_def(id);
|
||||
if ty::type_structurally_contains(ccx.tcx, rty, {|sty|
|
||||
alt sty {
|
||||
@ -3453,10 +3478,13 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
|
||||
_ { false }
|
||||
}
|
||||
}) {
|
||||
ccx.tcx.sess.span_fatal(sp, "illegal recursive enum type. \
|
||||
wrap the inner value in a box to \
|
||||
make it represenable");
|
||||
ccx.tcx.sess.span_err(sp, "illegal recursive enum type. \
|
||||
wrap the inner value in a box to \
|
||||
make it represenable");
|
||||
}
|
||||
|
||||
// Check that it is possible to instantiate this enum:
|
||||
check_instantiable(ccx.tcx, sp, id);
|
||||
}
|
||||
|
||||
// A generic function for checking the pred in a check
|
||||
@ -3672,6 +3700,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
|
||||
check_fn(ccx, ast::proto_bare, decl, body, it.id, false, none);
|
||||
}
|
||||
ast::item_res(decl, tps, body, dtor_id, _) {
|
||||
check_instantiable(ccx.tcx, it.span, it.id);
|
||||
check_fn(ccx, ast::proto_bare, decl, body, dtor_id, false, none);
|
||||
}
|
||||
ast::item_impl(tps, _, ty, ms) {
|
||||
|
12
src/test/compile-fail/issue-2063-resource.rs
Normal file
12
src/test/compile-fail/issue-2063-resource.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
resource t(x: x) {} //! ERROR this type cannot be instantiated
|
||||
enum x = @t; //! ERROR this type cannot be instantiated
|
||||
|
||||
fn new_t(x: t) {
|
||||
x.to_str; //! ERROR attempted access of field to_str
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
18
src/test/compile-fail/issue-2063.rs
Normal file
18
src/test/compile-fail/issue-2063.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// 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.
|
||||
enum t = @t; //! ERROR this type cannot be instantiated
|
||||
|
||||
// I use an impl here because it will cause
|
||||
// the compiler to attempt autoderef and then
|
||||
// try to resolve the method.
|
||||
impl methods for t {
|
||||
fn to_str() -> str { "t" }
|
||||
}
|
||||
|
||||
fn new_t(x: t) {
|
||||
x.to_str();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
export foo;
|
||||
export main;
|
||||
|
||||
enum list_cell<T> { cons(@list_cell<T>), }
|
||||
enum list_cell<T> { cons(@list_cell<T>), nil }
|
||||
|
||||
fn main() { }
|
||||
|
Loading…
Reference in New Issue
Block a user