Stop inferring bot/static when types/regions are unconstrained.

Also, some other changes that came up along the way:
- add a 'blk' region for the current block.
- detect unused type/region variables.
This commit is contained in:
Niko Matsakis 2012-04-30 10:37:58 -07:00
parent 079c3b02a8
commit 2db4259b35
30 changed files with 234 additions and 154 deletions

View File

@ -14,7 +14,7 @@ native mod rustrt {
fn rust_task_unweaken(ch: rust_port_id);
}
type global_ptr<T: send> = *libc::uintptr_t;
type global_ptr = *libc::uintptr_t;
#[doc = "
Atomically gets a channel from a pointer to a pointer-sized memory location
@ -22,7 +22,7 @@ or, if no channel exists creates and installs a new channel and sets up a new
task to receive from it.
"]
unsafe fn chan_from_global_ptr<T: send>(
global: global_ptr<T>,
global: global_ptr,
builder: fn() -> task::builder,
f: fn~(comm::port<T>)
) -> comm::chan<T> {

View File

@ -1168,7 +1168,9 @@ mod unsafe {
#[inline(always)]
unsafe fn form_slice<T,U>(p: *T, len: uint, f: fn([T]/&) -> U) -> U {
let pair = (p, len * sys::size_of::<T>());
let v : *([T]/&) = ::unsafe::reinterpret_cast(ptr::addr_of(pair));
// FIXME: should use &blk not &static here, but a snapshot is req'd
let v : *([T]/&static) =
::unsafe::reinterpret_cast(ptr::addr_of(pair));
f(*v)
}
}

View File

@ -452,11 +452,7 @@ enum prim_ty {
type region = {id: node_id, node: region_};
#[auto_serialize]
enum region_ {
re_anon,
re_named(ident),
re_static
}
enum region_ { re_anon, re_named(ident) }
#[auto_serialize]
enum ty_ {

View File

@ -250,16 +250,7 @@ fn parse_ret_ty(p: parser) -> (ast::ret_style, @ast::ty) {
fn region_from_name(p: parser, s: option<str>) -> @ast::region {
let r = alt s {
some (string) {
// FIXME: To be consistent with our type resolution, the
// static region should probably be resolved during type
// checking, not in the parser. (Issue #2256)
if string == "static" {
ast::re_static
} else {
ast::re_named(string)
}
}
some (string) { ast::re_named(string) }
none { ast::re_anon }
};

View File

@ -329,7 +329,6 @@ fn print_native_mod(s: ps, nmod: ast::native_mod, attrs: [ast::attribute]) {
fn print_region(s: ps, region: @ast::region) {
alt region.node {
ast::re_anon { word_space(s, "&"); }
ast::re_static { word_space(s, "&static"); }
ast::re_named(name) {
word(s.s, "&");
word_space(s, name);

View File

@ -251,23 +251,23 @@ fn find_library_crate_aux(sess: session::session,
if !(str::starts_with(f, prefix) && str::ends_with(f, suffix)) {
#debug("skipping %s, doesn't look like %s*%s", path, prefix,
suffix);
option::none
option::none::<()>
} else {
#debug("%s is a candidate", path);
alt get_metadata_section(sess, path) {
option::some(cvec) {
if !crate_matches(cvec, metas, hash) {
#debug("skipping %s, metadata doesn't match", path);
option::none
option::none::<()>
} else {
#debug("found %s with matching metadata", path);
matches += [{ident: path, data: cvec}];
option::none
option::none::<()>
}
}
_ {
#debug("could not load metadata for %s", path);
option::none
option::none::<()>
}
}
}

View File

@ -579,11 +579,8 @@ impl unify_methods for infer_ctxt {
// [B]. Deep resolution, on the other hand, would yield [int].
//
// But there is one more knob: the force_vars variable controls the
// behavior in the face of unconstrained variables. If we have A, B
// and only the constraint that A <: B, then the result is [_|_] if
// force_vars is true and [B] otherwise. We use force_vars == true
// when resolving types after typeck, but false otherwise (for
// example, when pretty-printing them for errors).
// behavior in the face of unconstrained variables. If it is true,
// then unconstrained variables result in an error.
type resolve_state = @{
infcx: infer_ctxt,
@ -673,8 +670,12 @@ impl methods for resolve_state {
let r1 = alt bounds {
{ ub:_, lb:some(t) } { self.resolve_region(t) }
{ ub:some(t), lb:_ } { self.resolve_region(t) }
{ ub:none, lb:none } if self.force_vars { ty::re_static }
{ ub:none, lb:none } { ty::re_var(rid) }
{ ub:none, lb:none } {
if self.force_vars {
self.err = some(unresolved_region(rid));
}
ty::re_var(rid)
}
};
vec::pop(self.r_seen);
ret r1;
@ -700,8 +701,12 @@ impl methods for resolve_state {
{ ub:_, lb:some(t) } if !type_is_bot(t) { self.resolve1(t) }
{ ub:some(t), lb:_ } { self.resolve1(t) }
{ ub:_, lb:some(t) } { self.resolve1(t) }
{ ub:none, lb:none } if self.force_vars { ty::mk_bot(tcx) }
{ ub:none, lb:none } { ty::mk_var(tcx, vid) }
{ ub:none, lb:none } {
if self.force_vars {
self.err = some(unresolved_ty(vid));
}
ty::mk_var(tcx, vid)
}
};
vec::pop(self.v_seen);
ret t1;

View File

@ -4,6 +4,7 @@ import syntax::codemap::span;
import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable};
import driver::session::session;
import std::map::hashmap;
import syntax::print::pprust::expr_to_str;
// Kind analysis pass. There are three kinds:
//
@ -138,6 +139,7 @@ fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
}
fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
#debug["kind::check_expr(%s)", expr_to_str(e)];
alt e.node {
expr_assign(_, ex) | expr_assign_op(_, _, ex) |
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |

View File

@ -37,7 +37,7 @@ export expr_ty_params_and_ty;
export expr_is_lval;
export field_ty;
export fold_ty, fold_sty_to_ty, fold_region, fold_regions, fold_ty_var;
export fold_regions_and_ty;
export fold_regions_and_ty, walk_regions_and_ty;
export field;
export field_idx;
export get_field;
@ -97,7 +97,7 @@ export ty_tup, mk_tup;
export ty_type, mk_type;
export ty_uint, mk_uint, mk_mach_uint;
export ty_uniq, mk_uniq, mk_imm_uniq, type_is_unique_box;
export ty_var, mk_var;
export ty_var, mk_var, type_is_var;
export ty_self, mk_self;
export region, bound_region;
export get, type_has_params, type_has_vars, type_has_regions;
@ -818,6 +818,21 @@ fn fold_ty_var(cx: ctxt, t0: t, fldop: fn(ty_vid) -> t) -> t {
}
}
fn walk_regions_and_ty(
cx: ctxt,
ty: t,
walkr: fn(r: region),
walkt: fn(t: t) -> bool) {
if (walkt(ty)) {
fold_regions_and_ty(
cx, ty,
{ |r| walkr(r); r },
{ |t| walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t },
{ |t| walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t });
}
}
fn fold_regions_and_ty(
cx: ctxt,
ty: t,
@ -1000,6 +1015,13 @@ fn type_is_nil(ty: t) -> bool { get(ty).struct == ty_nil }
fn type_is_bot(ty: t) -> bool { get(ty).struct == ty_bot }
fn type_is_var(ty: t) -> bool {
alt get(ty).struct {
ty_var(_) { true }
_ { false }
}
}
fn type_is_bool(ty: t) -> bool { get(ty).struct == ty_bool }
fn type_is_structural(ty: t) -> bool {

View File

@ -105,6 +105,7 @@ type fn_ctxt =
ty_var_counter: @mut uint,
region_var_counter: @mut uint,
mut blocks: [ast::node_id], // stack of blocks in scope, may be empty
in_scope_regions: isr_alist,
// While type checking a function, the intermediate types for the
@ -276,10 +277,8 @@ fn instantiate_path(fcx: @fn_ctxt,
// Type tests
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
alt infer::resolve_shallow(fcx.infcx, tp, true) {
// note: the bot type doesn't count as resolved; it's what we use when
// there is no information about a variable.
result::ok(t_s) if !ty::type_is_bot(t_s) { ret t_s; }
alt infer::resolve_shallow(fcx.infcx, tp, false) {
result::ok(t_s) if !ty::type_is_var(t_s) { ret t_s; }
_ {
fcx.ccx.tcx.sess.span_fatal
(sp, "the type of this value must be known in this context");
@ -322,7 +321,16 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
ast::vstore_uniq { ty::vstore_uniq }
ast::vstore_box { ty::vstore_box }
ast::vstore_slice(r) {
ty::vstore_slice(ast_region_to_region(fcx, fcx, e.span, r))
alt fcx.block_region() {
result::ok(b_r) {
let rscope = in_anon_rscope(fcx, b_r);
ty::vstore_slice(ast_region_to_region(fcx, rscope, e.span, r))
}
result::err(msg) {
fcx.ccx.tcx.sess.span_err(e.span, msg);
ty::vstore_slice(ty::re_static)
}
}
}
}
}
@ -386,8 +394,9 @@ impl of region_scope for empty_rscope {
fn anon_region() -> result<ty::region, str> {
result::err("region types are not allowed here")
}
fn named_region(_id: str) -> result<ty::region, str> {
result::err("region types are not allowed here")
fn named_region(id: str) -> result<ty::region, str> {
if id == "static" { result::ok(ty::re_static) }
else { result::err("only the static region is allowed here") }
}
}
@ -395,9 +404,7 @@ enum type_rscope = ast::region_param;
impl of region_scope for type_rscope {
fn anon_region() -> result<ty::region, str> {
alt *self {
ast::rp_self {
result::ok(ty::re_bound(ty::br_self))
}
ast::rp_self { result::ok(ty::re_bound(ty::br_self)) }
ast::rp_none {
result::err("to use region types here, the containing type \
must be declared with a region bound")
@ -405,11 +412,12 @@ impl of region_scope for type_rscope {
}
}
fn named_region(id: str) -> result<ty::region, str> {
if id == "self" {
self.anon_region()
} else {
result::err("named regions other than `self` are not \
allowed as part of a type declaration")
empty_rscope.named_region(id).chain_err { |_e|
if id == "self" { self.anon_region() }
else {
result::err("named regions other than `self` are not \
allowed as part of a type declaration")
}
}
}
}
@ -419,11 +427,14 @@ impl of region_scope for @fn_ctxt {
result::ok(self.next_region_var())
}
fn named_region(id: str) -> result<ty::region, str> {
alt self.in_scope_regions.find(ty::br_named(id)) {
some(r) { result::ok(r) }
none {
result::err(#fmt["named region `%s` not in scope here", id])
}
empty_rscope.named_region(id).chain_err { |_e|
alt self.in_scope_regions.find(ty::br_named(id)) {
some(r) { result::ok(r) }
none if id == "blk" { self.block_region() }
none {
result::err(#fmt["named region `%s` not in scope here", id])
}
}
}
}
}
@ -477,7 +488,6 @@ fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
let res = alt a_r.node {
ast::re_anon { rscope.anon_region() }
ast::re_named(id) { rscope.named_region(id) }
ast::re_static { result::ok(ty::re_static) }
};
get_region_reporting_err(self.tcx(), span, res)
@ -772,6 +782,49 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
ret typ;
}
fn check_bounds_are_used(ccx: @crate_ctxt,
span: span,
tps: [ast::ty_param],
rp: ast::region_param,
ty: ty::t) {
let mut r_used = alt rp {
ast::rp_self { false }
ast::rp_none { true }
};
if tps.len() == 0u && r_used { ret; }
let tps_used = vec::to_mut(vec::from_elem(tps.len(), false));
ty::walk_regions_and_ty(
ccx.tcx, ty,
{ |r|
alt r {
ty::re_bound(_) { r_used = true; }
_ { }
}
},
{ |t|
alt ty::get(t).struct {
ty::ty_param(idx, _) { tps_used[idx] = true; }
_ { }
}
true
});
if !r_used {
ccx.tcx.sess.span_err(
span, "lifetime `self` unused inside \
reference-parameterized type.");
}
for tps_used.eachi { |i, b|
if !b {
ccx.tcx.sess.span_err(
span, #fmt["Type parameter %s is unused.", tps[i].ident]);
}
}
}
fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
-> ty::ty_param_bounds_and_ty {
@ -816,6 +869,9 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
};
{bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty}
};
check_bounds_are_used(ccx, t.span, tps, rp, tpt.ty);
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
@ -1125,6 +1181,12 @@ impl methods for @fn_ctxt {
fn ty_to_str(t: ty::t) -> str {
ty_to_str(self.ccx.tcx, resolve_type_vars_if_possible(self, t))
}
fn block_region() -> result<ty::region, str> {
alt vec::last_opt(self.blocks) {
some(bid) { result::ok(ty::re_scope(bid)) }
none { result::err("no block is in scope here") }
}
}
fn write_ty(node_id: ast::node_id, ty: ty::t) {
#debug["write_ty(%d, %s) in fcx %s",
node_id, ty_to_str(self.tcx(), ty), self.tag()];
@ -3144,7 +3206,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
-> option<O> {
alt expected {
some(t) {
alt infer::resolve_shallow(fcx.infcx, t, true) {
alt infer::resolve_shallow(fcx.infcx, t, false) {
result::ok(t) { unpack(ty::get(t).struct) }
_ { none }
}
@ -3913,6 +3975,7 @@ fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
ast::unsafe_blk { @{purity: ast::unsafe_fn with *fcx0} }
ast::default_blk { fcx0 }
};
vec::push(fcx.blocks, blk.node.id);
let mut bot = false;
let mut warned = false;
for blk.node.stmts.each {|s|
@ -3943,6 +4006,7 @@ fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
if bot {
fcx.write_bot(blk.node.id);
}
vec::pop(fcx.blocks);
ret bot;
}
@ -3960,6 +4024,7 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
locals: int_hash(),
ty_var_counter: @mut 0u,
region_var_counter: @mut 0u,
mut blocks: [],
in_scope_regions: @nil,
node_types: smallintmap::mk(),
node_type_substs: map::int_hash(),
@ -4000,6 +4065,7 @@ fn check_enum_variants(ccx: @crate_ctxt,
locals: int_hash(),
ty_var_counter: @mut 0u,
region_var_counter: @mut 0u,
mut blocks: [],
in_scope_regions: @nil,
node_types: smallintmap::mk(),
node_type_substs: map::int_hash(),
@ -4262,6 +4328,7 @@ fn check_fn(ccx: @crate_ctxt,
locals: locals,
ty_var_counter: tvc,
region_var_counter: rvc,
mut blocks: [],
in_scope_regions: isr,
node_types: node_types,
node_type_substs: node_type_substs,
@ -4350,21 +4417,27 @@ fn check_fn(ccx: @crate_ctxt,
visit::visit_pat(p, e, v);
};
let visit_block = fn@(b: ast::blk, &&e: (), v: visit::vt<()>) {
vec::push(fcx.blocks, b.node.id);
visit::visit_block(b, e, v);
vec::pop(fcx.blocks);
};
// Don't descend into fns and items
fn visit_fn<T>(_fk: visit::fn_kind, _decl: ast::fn_decl,
_body: ast::blk, _sp: span,
_id: ast::node_id, _t: T, _v: visit::vt<T>) {
fn visit_fn(_fk: visit::fn_kind, _decl: ast::fn_decl,
_body: ast::blk, _sp: span,
_id: ast::node_id, &&_t: (), _v: visit::vt<()>) {
}
fn visit_item<E>(_i: @ast::item, _e: E, _v: visit::vt<E>) { }
fn visit_item(_i: @ast::item, &&_e: (), _v: visit::vt<()>) { }
let visit =
@{visit_local: visit_local,
visit_pat: visit_pat,
visit_fn: bind visit_fn(_, _, _, _, _, _, _),
visit_item: bind visit_item(_, _, _)
with *visit::default_visitor()};
let visit = visit::mk_vt(@{visit_local: visit_local,
visit_pat: visit_pat,
visit_fn: visit_fn,
visit_item: visit_item,
visit_block: visit_block
with *visit::default_visitor()});
visit::visit_block(body, (), visit::mk_vt(visit));
visit.visit_block(body, (), visit);
}
}

View File

@ -0,0 +1,7 @@
// Issue #1763 - infer types correctly
type actor<T> = { //! ERROR Type parameter T is unused.
unused: bool
};
fn main() {}

View File

@ -1,5 +1,5 @@
fn main(s: [str]) {
let a = [];
let a: [int] = [];
vec::each(a) { |x| //! ERROR in function `anon`, not all control paths
} //! ERROR see function return type of `bool`
}

View File

@ -0,0 +1,3 @@
type foo/& = {f: int}; //! ERROR lifetime `self` unused
fn main() {}

View File

@ -0,0 +1,15 @@
fn foo(cond: bool) {
let x = 5;
let mut y: &blk.int = &x;
let mut z: &blk.int;
if cond {
z = &x;
} else {
let w: &blk.int = &x;
z = w; //! ERROR mismatched types
}
}
fn main() {
}

View File

@ -0,0 +1,7 @@
// xfail-test
const c_x: &blk.int = 22; //! ERROR only the static region is allowed here
const c_y: &static.int = &22; //! ERROR only the static region is allowed here
fn main() {
}

View File

@ -18,7 +18,7 @@ type item_ty_yes1/& = {
x: &self.uint
};
type item_ty_yes2/& = {
type item_ty_yes2/& = { //! ERROR lifetime `self` unused inside reference-parameterized type
x: &foo.uint //! ERROR named regions other than `self` are not allowed as part of a type declaration
};

View File

@ -0,0 +1,13 @@
// xfail-test
fn foo(cond: bool) {
// Here we will infer a type that uses the
// region of the if stmt then block, but in the scope:
let mut x; //! ERROR foo
if cond {
x = [1,2,3]/&blk;
}
}
fn main() {}

View File

@ -0,0 +1,13 @@
// xfail-test
fn foo(cond: bool) {
// Here we will infer a type that uses the
// region of the if stmt then block:
let mut x; //! ERROR foo
if cond {
x = &3;
}
}
fn main() {}

View File

@ -4,7 +4,8 @@ fn send<T: send>(ch: _chan<T>, -data: T) {
log(debug, data);
fail;
}
type _chan<T> = int;
enum _chan<T> = int;
// Tests that "log(debug, message);" is flagged as using
// message after the send deinitializes it

View File

@ -0,0 +1,3 @@
fn main() {
let _foo = []; //! ERROR unconstrained type
}

View File

@ -4,7 +4,7 @@ fn test00_start(ch: chan_t<int>, message: int) { send(ch, copy message); }
type task_id = int;
type port_id = int;
type chan_t<T: send> = {task: task_id, port: port_id};
enum chan_t<T: send> = {task: task_id, port: port_id};
fn send<T: send>(ch: chan_t<T>, -data: T) { fail; }

View File

@ -1,14 +0,0 @@
// Issue #1763 - infer types correctly
// error-pattern:explicit failure
type actor<T> = {
unused: bool
};
fn act2<T>() -> actor<T> {
fail;
}
fn main() {
let a: actor<int> = act2();
}

View File

@ -1,4 +1,4 @@
// error-pattern:get called on error result: "kitty"
fn main() {
log(error, result::get(result::err("kitty")));
log(error, result::get(result::err::<int,str>("kitty")));
}

View File

@ -1,3 +1,5 @@
// xfail-test
// xfail-fast
// aux-build:cci_class_6.rs
use cci_class_6;

View File

@ -1,7 +1,8 @@
fn main() {
assert [1u, 3u].to_vec() == [1u, 3u];
assert [].to_vec() == [];
assert none.to_vec() == [];
let e: [uint] = [];
assert e.to_vec() == [];
assert none::<uint>.to_vec() == [];
assert some(1u).to_vec() == [1u];
assert some(2u).to_vec() == [2u];
}

View File

@ -16,7 +16,7 @@ type ccx = {
};
fn alloc(_bcx : &a.arena) -> &a.bcx unsafe {
ret unsafe::reinterpret_cast(libc::malloc(sys::size_of::<bcx>()));
ret unsafe::reinterpret_cast(libc::malloc(sys::size_of::<bcx/&blk>()));
}
fn h(bcx : &a.bcx) -> &a.bcx {

View File

@ -8,7 +8,6 @@ fn call_id() {
}
fn call_id_3() { id(ret) && id(ret); }
//!^ ERROR the type of this value must be known
fn main() {
}

View File

@ -1,57 +0,0 @@
// xfail-pretty
fn id(x: bool) -> bool { x }
fn call_id() {
let c <- fail;
id(c);
}
fn call_id_2() { id(true) && id(ret); }
fn call_id_4() { while id(ret) { } }
fn bind_id_1() { bind id(fail); }
fn bind_id_2() { bind id(ret); }
fn fail_fail() { fail fail; }
fn log_fail() { log(error, fail); }
fn log_ret() { log(error, ret); }
fn log_break() { loop { log(error, break); } }
fn log_cont() { do { log(error, cont); } while false }
fn ret_ret() -> int { ret 3 + (ret 2); }
fn ret_guard() {
alt check 2 {
x if (ret) { x; }
}
}
fn rec_ret() { let _r: {c: int} = {c: ret}; }
fn vec_ret() { let _v: [int] = [1, 2, ret, 4]; }
fn fail_then_concat() {
let mut x = [], y = [3];
fail;
x += y;
"good" + "bye";
}
fn main() {
// Call the functions that don't fail.
rec_ret();
vec_ret();
ret_ret();
log_ret();
call_id_2();
call_id_4();
bind_id_2();
ret_guard();
}

View File

@ -1,4 +0,0 @@
fn main() {
// We will infer this to have the type vec[bot]
let _foo = [];
}

View File

@ -1,6 +1,6 @@
// Just a grab bag of stuff that you wouldn't want to actually write.
fn strange() -> bool { let _x = ret true; }
fn strange() -> bool { let _x: bool = ret true; }
fn funny() {
fn f(_x: ()) { }
@ -20,13 +20,14 @@ fn zombiejesus() {
while (ret) {
if (ret) {
alt (ret) {
_ {
1 {
if (ret) {
ret
} else {
ret
}
}
_ { ret }
};
} else if (ret) {
ret;
@ -51,7 +52,7 @@ fn canttouchthis() -> uint {
pure fn p() -> bool { true }
let _a = (assert (true)) == (check (p()));
let _c = (check (p())) == ();
let _b = (log(debug, 0) == (ret 0u));
let _b: bool = (log(debug, 0) == (ret 0u));
}
fn angrydome() {