add static region and also fix regions to be contravariant
This commit is contained in:
parent
f1afb0b3e2
commit
051f24da25
@ -442,7 +442,8 @@ enum prim_ty {
|
||||
enum region_ {
|
||||
re_inferred,
|
||||
re_named(ident),
|
||||
re_self
|
||||
re_self,
|
||||
re_static
|
||||
}
|
||||
|
||||
#[auto_serialize]
|
||||
|
@ -434,6 +434,8 @@ fn parse_region(p: parser) -> ast::region {
|
||||
p.bump(); p.bump();
|
||||
if string == "self" {
|
||||
ast::re_self
|
||||
} else if string == "static" {
|
||||
ast::re_static
|
||||
} else {
|
||||
ast::re_named(string)
|
||||
}
|
||||
|
@ -322,6 +322,7 @@ fn print_region(s: ps, region: ast::region) {
|
||||
ast::re_inferred { /* no-op */ }
|
||||
ast::re_named(name) { word(s.s, name); word(s.s, "."); }
|
||||
ast::re_self { word(s.s, "self"); word(s.s, "."); }
|
||||
ast::re_static { word(s.s, "static"); word(s.s, "."); }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,9 @@ fn enc_region(w: io::writer, r: ty::region) {
|
||||
w.write_uint(id.to_uint());
|
||||
w.write_char('|');
|
||||
}
|
||||
ty::re_static {
|
||||
w.write_char('t');
|
||||
}
|
||||
}
|
||||
}
|
||||
fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
import result::{result, extensions, ok, err, map, map2, iter2};
|
||||
import ty::type_is_bot;
|
||||
import driver::session::session;
|
||||
import util::common::{indent, indenter};
|
||||
|
||||
export infer_ctxt;
|
||||
export new_infer_ctxt;
|
||||
@ -66,25 +67,31 @@ fn new_infer_ctxt(tcx: ty::ctxt) -> infer_ctxt {
|
||||
}
|
||||
|
||||
fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
|
||||
#debug[">> mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
||||
cx.commit {||
|
||||
cx.tys(a, b)
|
||||
#debug["mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
||||
indent {||
|
||||
cx.commit {||
|
||||
cx.tys(a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_eqty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
|
||||
#debug[">> mk_eqty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
||||
cx.commit {||
|
||||
cx.eq_tys(a, b)
|
||||
#debug["mk_eqty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
||||
indent {||
|
||||
cx.commit {||
|
||||
cx.eq_tys(a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_tys(tcx: ty::ctxt, a: ty::t, b: ty::t) -> ures {
|
||||
let infcx = new_infer_ctxt(tcx);
|
||||
#debug[">> compare_tys(%s == %s)", a.to_str(infcx), b.to_str(infcx)];
|
||||
infcx.commit {||
|
||||
mk_subty(infcx, a, b).then {||
|
||||
mk_subty(infcx, b, a)
|
||||
#debug["compare_tys(%s == %s)", a.to_str(infcx), b.to_str(infcx)];
|
||||
indent {||
|
||||
infcx.commit {||
|
||||
mk_subty(infcx, a, b).then {||
|
||||
mk_subty(infcx, b, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,24 +233,22 @@ fn set_ty(vid: ty_vid, +new_v: var_value<ty_vid, ty::t>) {
|
||||
self.set(self.vb, vid, new_v);
|
||||
}
|
||||
|
||||
fn commit<T:copy,E:copy>(f: fn() -> result<T,E>) -> result<T,E> {
|
||||
fn commit<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
|
||||
|
||||
assert self.vb.bindings.len() == 0u;
|
||||
assert self.rb.bindings.len() == 0u;
|
||||
|
||||
let r = self.try(f);
|
||||
let r <- self.try(f);
|
||||
|
||||
// TODO---could use a vec::clear() that ran destructors but kept
|
||||
// the vec at its currently allocated length
|
||||
self.vb.bindings = [];
|
||||
self.rb.bindings = [];
|
||||
|
||||
#debug[">> Commit result: %?", r];
|
||||
|
||||
ret r;
|
||||
}
|
||||
|
||||
fn try<T:copy,E:copy>(f: fn() -> result<T,E>) -> result<T,E> {
|
||||
fn try<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
|
||||
|
||||
fn rollback_to<V:copy vid, T:copy>(
|
||||
vb: vals_and_bindings<V, T>, len: uint) {
|
||||
@ -257,7 +262,7 @@ fn rollback_to<V:copy vid, T:copy>(
|
||||
let vbl = self.vb.bindings.len();
|
||||
let rbl = self.rb.bindings.len();
|
||||
#debug["try(vbl=%u, rbl=%u)", vbl, rbl];
|
||||
let r = f();
|
||||
let r <- f();
|
||||
alt r {
|
||||
result::ok(_) { #debug["try--ok"]; }
|
||||
result::err(_) {
|
||||
@ -309,6 +314,9 @@ fn merge_bnd<V:copy to_str>(
|
||||
a: bound<V>, b: bound<V>,
|
||||
merge_op: fn(V,V) -> cres<V>) -> cres<bound<V>> {
|
||||
|
||||
#debug["merge_bnd(%s,%s)", a.to_str(self), b.to_str(self)];
|
||||
let _r = indenter();
|
||||
|
||||
alt (a, b) {
|
||||
(none, none) {
|
||||
ok(none)
|
||||
@ -332,6 +340,7 @@ fn merge_bnds<V:copy to_str>(
|
||||
lub: fn(V,V) -> cres<V>,
|
||||
glb: fn(V,V) -> cres<V>) -> cres<bounds<V>> {
|
||||
|
||||
let _r = indenter();
|
||||
self.merge_bnd(a.ub, b.ub, glb).chain {|ub|
|
||||
#debug["glb of ubs %s and %s is %s",
|
||||
a.ub.to_str(self), b.ub.to_str(self),
|
||||
@ -383,6 +392,7 @@ fn set_var_to_merged_bounds<V:copy vid, T:copy to_str st>(
|
||||
// them explicitly gives the type inferencer more
|
||||
// information and helps to produce tighter bounds
|
||||
// when necessary.
|
||||
indent {||
|
||||
self.bnds(a.lb, b.ub).then {||
|
||||
self.bnds(b.lb, a.ub).then {||
|
||||
self.merge_bnd(a.ub, b.ub, {|x, y| x.glb(self, y)}).chain {|ub|
|
||||
@ -395,10 +405,10 @@ fn set_var_to_merged_bounds<V:copy vid, T:copy to_str st>(
|
||||
// the new bounds must themselves
|
||||
// be relatable:
|
||||
self.bnds(bnds.lb, bnds.ub).then {||
|
||||
self.set(vb, v_id, bounded(bnds));
|
||||
self.uok()
|
||||
self.set(vb, v_id, bounded(bnds));
|
||||
self.uok()
|
||||
}
|
||||
}}}}
|
||||
}}}}}
|
||||
}
|
||||
|
||||
fn vars<V:copy vid, T:copy to_str st>(
|
||||
@ -465,46 +475,29 @@ fn tvar<V: copy vid, T: copy to_str st>(
|
||||
}
|
||||
|
||||
fn regions(a: ty::region, b: ty::region) -> ures {
|
||||
alt (a, b) { // XXX
|
||||
(ty::re_var(a_id), ty::re_var(b_id)) {
|
||||
self.vars(self.rb, a_id, b_id)
|
||||
}
|
||||
(ty::re_var(a_id), _) {
|
||||
self.vart(self.rb, a_id, b)
|
||||
}
|
||||
(_, ty::re_var(b_id)) {
|
||||
self.tvar(self.rb, a, b_id)
|
||||
}
|
||||
#debug["regions(%s <= %s)", a.to_str(self), b.to_str(self)];
|
||||
indent {||
|
||||
alt (a, b) {
|
||||
(ty::re_var(a_id), ty::re_var(b_id)) {
|
||||
self.vars(self.rb, a_id, b_id)
|
||||
}
|
||||
(ty::re_var(a_id), _) {
|
||||
self.vart(self.rb, a_id, b)
|
||||
}
|
||||
(_, ty::re_var(b_id)) {
|
||||
self.tvar(self.rb, a, b_id)
|
||||
}
|
||||
|
||||
(ty::re_free(a_id, _), ty::re_scope(b_id)) |
|
||||
(ty::re_scope(a_id), ty::re_free(b_id, _)) |
|
||||
(ty::re_scope(a_id), ty::re_scope(b_id)) {
|
||||
let rm = self.tcx.region_map;
|
||||
alt region::nearest_common_ancestor(rm, a_id, b_id) {
|
||||
some(r_id) if r_id == a_id { self.uok() }
|
||||
_ { err(ty::terr_regions_differ(false, b, a)) }
|
||||
_ {
|
||||
lub(self).c_regions(a, b).chain {|r|
|
||||
if b == r {
|
||||
self.uok()
|
||||
} else {
|
||||
err(ty::terr_regions_differ(false, b, a))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For these types, we cannot define any additional relationship:
|
||||
(ty::re_free(_, _), ty::re_free(_, _)) |
|
||||
(ty::re_bound(_), ty::re_bound(_)) |
|
||||
(ty::re_bound(_), ty::re_free(_, _)) |
|
||||
(ty::re_bound(_), ty::re_scope(_)) |
|
||||
(ty::re_free(_, _), ty::re_bound(_)) |
|
||||
(ty::re_scope(_), ty::re_bound(_)) {
|
||||
if a == b {
|
||||
self.uok()
|
||||
} else {
|
||||
err(ty::terr_regions_differ(false, b, a))
|
||||
}
|
||||
}
|
||||
|
||||
(ty::re_default, _) |
|
||||
(_, ty::re_default) {
|
||||
// actually a compiler bug, I think.
|
||||
err(ty::terr_regions_differ(false, b, a))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,16 +650,17 @@ fn bnds<T:copy to_str st>(
|
||||
a: bound<T>, b: bound<T>) -> ures {
|
||||
|
||||
#debug("bnds(%s <: %s)", a.to_str(self), b.to_str(self));
|
||||
|
||||
alt (a, b) {
|
||||
(none, none) |
|
||||
(some(_), none) |
|
||||
(none, some(_)) {
|
||||
self.uok()
|
||||
}
|
||||
(some(t_a), some(t_b)) {
|
||||
t_a.st(self, t_b)
|
||||
}
|
||||
indent {||
|
||||
alt (a, b) {
|
||||
(none, none) |
|
||||
(some(_), none) |
|
||||
(none, some(_)) {
|
||||
self.uok()
|
||||
}
|
||||
(some(t_a), some(t_b)) {
|
||||
t_a.st(self, t_b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -744,7 +738,9 @@ fn tys(a: ty::t, b: ty::t) -> ures {
|
||||
|
||||
(ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) {
|
||||
self.mts(a_mt, b_mt).then {||
|
||||
self.regions(a_r, b_r)
|
||||
// Non-obvious: for &a.T to be a subtype of &b.T, &a
|
||||
// must exist for LONGER than &b. That is, &b <= &a.
|
||||
self.regions(b_r, a_r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -972,8 +968,11 @@ fn fixup_ty(typ: ty::t) -> fres<ty::t> {
|
||||
|
||||
// Combining regions (along with some specific cases that are
|
||||
// different for LUB/GLB):
|
||||
fn c_contraregions(
|
||||
a: ty::region, b: ty::region) -> cres<ty::region>;
|
||||
fn c_regions(
|
||||
a: ty::region, b: ty::region) -> cres<ty::region>;
|
||||
fn c_regions_static(r: ty::region) -> cres<ty::region>;
|
||||
fn c_regions_scope_scope(
|
||||
a: ty::region, a_id: ast::node_id,
|
||||
b: ty::region, b_id: ast::node_id) -> cres<ty::region>;
|
||||
@ -1045,11 +1044,13 @@ fn c_var_t<V:copy vid, C:combine, T:copy to_str st>(
|
||||
alt self.bnd(a_bounds) {
|
||||
some(a_bnd) {
|
||||
// If a has an upper bound, return it.
|
||||
#debug["bnd=some(%s)", a_bnd.to_str(self.infcx())];
|
||||
ret c_ts(a_bnd, b);
|
||||
}
|
||||
none {
|
||||
// If a does not have an upper bound, make b the upper bound of a
|
||||
// and then return b.
|
||||
#debug["bnd=none"];
|
||||
let a_bounds = self.with_bnd(a_bounds, b);
|
||||
self.infcx().bnds(a_bounds.lb, a_bounds.ub).then {||
|
||||
self.infcx().set(vb, a_id, bounded(a_bounds));
|
||||
@ -1162,6 +1163,7 @@ fn c_tys<C:combine>(
|
||||
// Fast path.
|
||||
if a == b { ret ok(a); }
|
||||
|
||||
indent {||
|
||||
alt (ty::get(a).struct, ty::get(b).struct) {
|
||||
(ty::ty_bot, _) { self.c_bot(b) }
|
||||
(_, ty::ty_bot) { self.c_bot(b) }
|
||||
@ -1249,7 +1251,7 @@ fn c_tys<C:combine>(
|
||||
}
|
||||
|
||||
(ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) {
|
||||
self.c_regions(a_r, b_r).chain {|r|
|
||||
self.c_contraregions(a_r, b_r).chain {|r|
|
||||
self.c_mts(a_mt, b_mt).chain {|mt|
|
||||
ok(ty::mk_rptr(tcx, r, mt))
|
||||
}
|
||||
@ -1293,6 +1295,7 @@ fn c_tys<C:combine>(
|
||||
|
||||
_ { err(ty::terr_mismatch) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn c_regions<C:combine>(
|
||||
@ -1303,7 +1306,12 @@ fn c_regions<C:combine>(
|
||||
a.to_str(self.infcx()),
|
||||
b.to_str(self.infcx())];
|
||||
|
||||
indent {||
|
||||
alt (a, b) {
|
||||
(ty::re_static, r) | (r, ty::re_static) {
|
||||
self.c_regions_static(r)
|
||||
}
|
||||
|
||||
(ty::re_var(a_id), ty::re_var(b_id)) {
|
||||
c_vars(self, self.infcx().rb,
|
||||
a, a_id, b_id,
|
||||
@ -1352,6 +1360,7 @@ fn c_regions<C:combine>(
|
||||
err(ty::terr_regions_differ(false, b, a))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl of combine for lub {
|
||||
@ -1443,6 +1452,16 @@ fn c_regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
ret c_regions(self, a, b);
|
||||
}
|
||||
|
||||
fn c_contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
ret glb(self.infcx()).c_regions(a, b);
|
||||
}
|
||||
|
||||
fn c_regions_static(_r: ty::region) -> cres<ty::region> {
|
||||
// LUB of `r` and static is always static---what's bigger than
|
||||
// that?
|
||||
ret ok(ty::re_static);
|
||||
}
|
||||
|
||||
fn c_regions_free_scope(
|
||||
a: ty::region, _a_id: ast::node_id, _a_br: ty::bound_region,
|
||||
_b: ty::region, _b_id: ast::node_id) -> cres<ty::region> {
|
||||
@ -1573,6 +1592,16 @@ fn c_regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
ret c_regions(self, a, b);
|
||||
}
|
||||
|
||||
fn c_contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
||||
ret lub(self.infcx()).c_regions(a, b);
|
||||
}
|
||||
|
||||
fn c_regions_static(r: ty::region) -> cres<ty::region> {
|
||||
// GLB of `r` and static is always `r`; static is bigger than
|
||||
// everything
|
||||
ret ok(r);
|
||||
}
|
||||
|
||||
fn c_regions_free_scope(
|
||||
_a: ty::region, _a_id: ast::node_id, _a_br: ty::bound_region,
|
||||
b: ty::region, _b_id: ast::node_id) -> cres<ty::region> {
|
||||
|
@ -377,6 +377,8 @@ fn resolve_region_binding(cx: ctxt, span: span, region: ast::region) {
|
||||
// }
|
||||
}
|
||||
|
||||
ast::re_static { /* fallthrough */ }
|
||||
|
||||
ast::re_named(ident) {
|
||||
alt cx.scope.resolve_ident(ident) {
|
||||
some(r) {
|
||||
|
@ -28,7 +28,7 @@ fn check_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
|
||||
alt ty::get(t).struct {
|
||||
ty::ty_rptr(region, _) {
|
||||
alt region {
|
||||
ty::re_bound(_) | ty::re_free(_, _) {
|
||||
ty::re_bound(_) | ty::re_free(_, _) | ty::re_static {
|
||||
/* ok */
|
||||
}
|
||||
ty::re_scope(rbi) {
|
||||
|
@ -267,6 +267,7 @@ enum region {
|
||||
re_free(node_id, bound_region),
|
||||
re_scope(node_id),
|
||||
re_var(region_vid),
|
||||
re_static, // effectively `top` in the region lattice
|
||||
re_default
|
||||
}
|
||||
|
||||
@ -1473,6 +1474,7 @@ fn hash_region(r: region) -> uint {
|
||||
re_scope(id) { ((id as uint) << 2u) | 2u }
|
||||
re_var(id) { (id.to_uint() << 2u) | 3u }
|
||||
re_default { 4u }
|
||||
re_bot { 5u }
|
||||
}
|
||||
}
|
||||
alt st {
|
||||
|
@ -20,6 +20,7 @@
|
||||
import std::serialization::{serialize_uint, deserialize_uint};
|
||||
import std::ufind;
|
||||
import syntax::print::pprust::*;
|
||||
import util::common::indent;
|
||||
|
||||
export check_crate;
|
||||
export method_map;
|
||||
@ -366,6 +367,9 @@ fn do_ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty)
|
||||
ast::re_self | ast::re_named(_) {
|
||||
tcx.region_map.ast_type_to_region.get(region.id)
|
||||
}
|
||||
ast::re_static {
|
||||
ty::re_static
|
||||
}
|
||||
};
|
||||
ty::mk_rptr(tcx, r, ast_mt_to_mt(tcx, mode, mt))
|
||||
}
|
||||
@ -2472,7 +2476,7 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
|
||||
fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
||||
expected: ty::t) -> bool {
|
||||
|
||||
#debug("typechecking expr %d (%s)",
|
||||
#debug(">> typechecking expr %d (%s)",
|
||||
expr.id, syntax::print::pprust::expr_to_str(expr));
|
||||
|
||||
// A generic function to factor out common logic from call and bind
|
||||
@ -3382,6 +3386,8 @@ fn get_node(f: spanned<field>) -> field { f.node }
|
||||
ty_to_str(tcx, expected));
|
||||
|
||||
unify(fcx, expr.span, expected, fcx.expr_ty(expr));
|
||||
|
||||
#debug("<< bot=%b", bot);
|
||||
ret bot;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ fn region_to_str(cx: ctxt, region: region) -> str {
|
||||
// These two should not be seen by end-users (very often, anyhow):
|
||||
re_var(id) { #fmt("&%s.", id.to_str()) }
|
||||
re_default { "&(default)." }
|
||||
re_static { "&static." }
|
||||
}
|
||||
}
|
||||
|
||||
|
19
src/test/compile-fail/regions-leaking-ptr.rs
Normal file
19
src/test/compile-fail/regions-leaking-ptr.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// The type of `y` ends up getting inferred to the type of the block.
|
||||
// This generates a ton of error msgs at the moment.
|
||||
fn broken() -> int {
|
||||
let mut x = 3;
|
||||
let mut y = [&x]; //! ERROR reference escapes its block
|
||||
while x < 10 {
|
||||
let z = x;
|
||||
y += [&z];
|
||||
x += 1;
|
||||
}
|
||||
vec::foldl(0, y) {|v, p| v + *p }
|
||||
//!^ ERROR reference escapes its block
|
||||
//!^^ ERROR reference escapes its block
|
||||
//!^^^ ERROR reference escapes its block
|
||||
//!^^^^ ERROR reference escapes its block
|
||||
//!^^^^^ ERROR reference escapes its block
|
||||
}
|
||||
|
||||
fn main() { }
|
8
src/test/run-pass/regions-bot.rs
Normal file
8
src/test/run-pass/regions-bot.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// A very limited test of the "bottom" region
|
||||
|
||||
fn produce_static<T>() -> &static.T { fail; }
|
||||
|
||||
fn foo<T>(x: &T) -> &uint { produce_static() }
|
||||
|
||||
fn main() {
|
||||
}
|
Loading…
Reference in New Issue
Block a user