Skip to content

Commit 23f92ea

Browse files
committed
Detect and report types which could never be instantiated.
Fixes #2063.
1 parent 8cf44be commit 23f92ea

File tree

5 files changed

+194
-4
lines changed

5 files changed

+194
-4
lines changed

src/rustc/middle/ty.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import util::ppaux::ty_to_str;
1515
import util::ppaux::ty_constr_to_str;
1616
import syntax::print::pprust::*;
1717

18+
export is_instantiable;
1819
export node_id_to_type;
1920
export node_id_to_type_params;
2021
export arg;
@@ -1007,6 +1008,136 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
10071008
ret result;
10081009
}
10091010

1011+
// True if instantiating an instance of `ty` requires an instead of `r_ty`.
1012+
fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
1013+
1014+
fn type_requires(cx: ctxt, seen: @mut [def_id],
1015+
r_ty: t, ty: t) -> bool {
1016+
#debug["type_requires(%s, %s)?",
1017+
ty_to_str(cx, r_ty),
1018+
ty_to_str(cx, ty)];
1019+
1020+
let r = {
1021+
get(r_ty).struct == get(ty).struct ||
1022+
subtypes_require(cx, seen, r_ty, ty)
1023+
};
1024+
1025+
#debug["type_requires(%s, %s)? %b",
1026+
ty_to_str(cx, r_ty),
1027+
ty_to_str(cx, ty),
1028+
r];
1029+
ret r;
1030+
}
1031+
1032+
fn subtypes_require(cx: ctxt, seen: @mut [def_id],
1033+
r_ty: t, ty: t) -> bool {
1034+
#debug["subtypes_require(%s, %s)?",
1035+
ty_to_str(cx, r_ty),
1036+
ty_to_str(cx, ty)];
1037+
1038+
let r = alt get(ty).struct {
1039+
ty_nil |
1040+
ty_bot |
1041+
ty_bool |
1042+
ty_int(_) |
1043+
ty_uint(_) |
1044+
ty_float(_) |
1045+
ty_str |
1046+
ty_fn(_) |
1047+
ty_var(_) |
1048+
ty_param(_, _) |
1049+
ty_self(_) |
1050+
ty_type |
1051+
ty_opaque_box |
1052+
ty_opaque_closure_ptr(_) |
1053+
ty_vec(_) {
1054+
false
1055+
}
1056+
1057+
ty_constr(t, _) {
1058+
type_requires(cx, seen, r_ty, t)
1059+
}
1060+
1061+
ty_box(mt) |
1062+
ty_uniq(mt) |
1063+
ty_ptr(mt) |
1064+
ty_rptr(_, mt) {
1065+
be type_requires(cx, seen, r_ty, mt.ty);
1066+
}
1067+
1068+
ty_rec(fields) {
1069+
vec::any(fields) {|field|
1070+
type_requires(cx, seen, r_ty, field.mt.ty)
1071+
}
1072+
}
1073+
1074+
ty_iface(_, _) {
1075+
false
1076+
}
1077+
1078+
ty_class(did, _) if vec::contains(*seen, did) {
1079+
false
1080+
}
1081+
1082+
ty_class(did, tps) {
1083+
vec::push(*seen, did);
1084+
let r = vec::any(lookup_class_fields(cx, did)) {|f|
1085+
let fty = ty::lookup_item_type(cx, f.id);
1086+
let sty = substitute_type_params(cx, tps, fty.ty);
1087+
type_requires(cx, seen, r_ty, sty)
1088+
};
1089+
vec::pop(*seen);
1090+
r
1091+
}
1092+
1093+
ty_res(did, _, _) if vec::contains(*seen, did) {
1094+
false
1095+
}
1096+
1097+
ty_res(did, sub, tps) {
1098+
vec::push(*seen, did);
1099+
let sty = substitute_type_params(cx, tps, sub);
1100+
let r = type_requires(cx, seen, r_ty, sty);
1101+
vec::pop(*seen);
1102+
r
1103+
}
1104+
1105+
ty_tup(ts) {
1106+
vec::any(ts) {|t|
1107+
type_requires(cx, seen, r_ty, t)
1108+
}
1109+
}
1110+
1111+
ty_enum(did, _) if vec::contains(*seen, did) {
1112+
false
1113+
}
1114+
1115+
ty_enum(did, tps) {
1116+
vec::push(*seen, did);
1117+
let vs = enum_variants(cx, did);
1118+
let r = vec::len(*vs) > 0u && vec::all(*vs) {|variant|
1119+
vec::any(variant.args) {|aty|
1120+
let sty = substitute_type_params(cx, tps, aty);
1121+
type_requires(cx, seen, r_ty, sty)
1122+
}
1123+
};
1124+
vec::pop(*seen);
1125+
r
1126+
}
1127+
};
1128+
1129+
#debug["subtypes_require(%s, %s)? %b",
1130+
ty_to_str(cx, r_ty),
1131+
ty_to_str(cx, ty),
1132+
r];
1133+
1134+
ret r;
1135+
}
1136+
1137+
let seen = @mut [];
1138+
!subtypes_require(cx, seen, r_ty, r_ty)
1139+
}
1140+
10101141
fn type_structurally_contains(cx: ctxt, ty: t, test: fn(sty) -> bool) ->
10111142
bool {
10121143
let sty = get(ty).struct;

src/rustc/middle/typeck.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,7 @@ mod unify {
11451145
// instead of ty::struct.
11461146
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
11471147
let mut t1 = t;
1148+
let mut enum_dids = [];
11481149
loop {
11491150
alt structure_of(fcx, sp, t1) {
11501151
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 {
11611162
t1 = ty::substitute_type_params(fcx.ccx.tcx, tps, inner);
11621163
}
11631164
ty::ty_enum(did, tps) {
1165+
// Watch out for a type like `enum t = @t`. Such a type would
1166+
// otherwise infinitely auto-deref. This is the only autoderef
1167+
// loop that needs to be concerned with this, as an error will be
1168+
// reported on the enum definition as well because the enum is not
1169+
// instantiable.
1170+
if vec::contains(enum_dids, did) {
1171+
ret t1;
1172+
}
1173+
vec::push(enum_dids, did);
1174+
11641175
let variants = ty::enum_variants(fcx.ccx.tcx, did);
11651176
if vec::len(*variants) != 1u || vec::len(variants[0].args) != 1u {
11661177
ret t1;
@@ -3396,6 +3407,18 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
33963407
demand::simple(fcx, e.span, declty, cty);
33973408
}
33983409

3410+
fn check_instantiable(tcx: ty::ctxt,
3411+
sp: span,
3412+
item_id: ast::node_id) {
3413+
let rty = ty::node_id_to_type(tcx, item_id);
3414+
if !ty::is_instantiable(tcx, rty) {
3415+
tcx.sess.span_err(sp, #fmt["this type cannot be instantiated \
3416+
without an instance of itself. \
3417+
Consider using option<%s>.",
3418+
ty_to_str(tcx, rty)]);
3419+
}
3420+
}
3421+
33993422
fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
34003423
id: ast::node_id) {
34013424
// 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],
34433466
disr_vals += [disr_val];
34443467
disr_val += 1;
34453468
}
3469+
3470+
// Check that it is possible to represent this enum:
34463471
let mut outer = true, did = local_def(id);
34473472
if ty::type_structurally_contains(ccx.tcx, rty, {|sty|
34483473
alt sty {
@@ -3453,10 +3478,13 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
34533478
_ { false }
34543479
}
34553480
}) {
3456-
ccx.tcx.sess.span_fatal(sp, "illegal recursive enum type. \
3457-
wrap the inner value in a box to \
3458-
make it represenable");
3481+
ccx.tcx.sess.span_err(sp, "illegal recursive enum type. \
3482+
wrap the inner value in a box to \
3483+
make it represenable");
34593484
}
3485+
3486+
// Check that it is possible to instantiate this enum:
3487+
check_instantiable(ccx.tcx, sp, id);
34603488
}
34613489

34623490
// A generic function for checking the pred in a check
@@ -3672,6 +3700,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
36723700
check_fn(ccx, ast::proto_bare, decl, body, it.id, false, none);
36733701
}
36743702
ast::item_res(decl, tps, body, dtor_id, _) {
3703+
check_instantiable(ccx.tcx, it.span, it.id);
36753704
check_fn(ccx, ast::proto_bare, decl, body, dtor_id, false, none);
36763705
}
36773706
ast::item_impl(tps, _, ty, ms) {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// test that autoderef of a type like this does not
2+
// cause compiler to loop. Note that no instances
3+
// of such a type could ever be constructed.
4+
resource t(x: x) {} //! ERROR this type cannot be instantiated
5+
enum x = @t; //! ERROR this type cannot be instantiated
6+
7+
fn new_t(x: t) {
8+
x.to_str; //! ERROR attempted access of field to_str
9+
}
10+
11+
fn main() {
12+
}

src/test/compile-fail/issue-2063.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// test that autoderef of a type like this does not
2+
// cause compiler to loop. Note that no instances
3+
// of such a type could ever be constructed.
4+
enum t = @t; //! ERROR this type cannot be instantiated
5+
6+
// I use an impl here because it will cause
7+
// the compiler to attempt autoderef and then
8+
// try to resolve the method.
9+
impl methods for t {
10+
fn to_str() -> str { "t" }
11+
}
12+
13+
fn new_t(x: t) {
14+
x.to_str();
15+
}
16+
17+
fn main() {
18+
}

src/test/run-pass/export-non-interference.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
export foo;
33
export main;
44

5-
enum list_cell<T> { cons(@list_cell<T>), }
5+
enum list_cell<T> { cons(@list_cell<T>), nil }
66

77
fn main() { }

0 commit comments

Comments
 (0)