Make let bindings' scope start at their definition

Also, allow let bindings in a block to shadow each other.
This commit is contained in:
Marijn Haverbeke 2011-08-16 13:24:04 +02:00
parent 92719e438c
commit e33af7e0b5
8 changed files with 70 additions and 28 deletions

View File

@ -53,7 +53,7 @@ tag scope {
scope_native_item(@ast::native_item);
scope_loop(@ast::local); // there's only 1 decl per loop.
scope_block(ast::blk);
scope_block(ast::blk, @mutable uint, @mutable uint);
scope_arm(ast::arm);
}
@ -254,6 +254,7 @@ fn resolve_names(e: &@env, c: &@ast::crate) {
@{visit_native_item: visit_native_item_with_scope,
visit_item: visit_item_with_scope,
visit_block: visit_block_with_scope,
visit_decl: visit_decl_with_scope,
visit_arm: bind walk_arm(e, _, _, _),
visit_pat: bind walk_pat(e, _, _, _),
visit_expr: bind walk_expr(e, _, _, _),
@ -353,7 +354,30 @@ fn visit_fn_with_scope(e: &@env, f: &ast::_fn, tp: &[ast::ty_param],
}
fn visit_block_with_scope(b: &ast::blk, sc: &scopes, v: &vt[scopes]) {
visit::visit_block(b, cons(scope_block(b), @sc), v);
let pos = @mutable 0u, loc = @mutable 0u;
let block_sc = cons(scope_block(b, pos, loc), @sc);
for stmt in b.node.stmts {
v.visit_stmt(stmt, block_sc, v);
*pos += 1u;
*loc = 0u;
}
visit::visit_expr_opt(b.node.expr, block_sc, v);
}
fn visit_decl_with_scope(d: &@decl, sc: &scopes, v: &vt[scopes]) {
let loc_pos = alt list::car(sc) {
scope_block(_, _, pos) { pos }
_ { @mutable 0u }
};
alt d.node {
decl_local(locs) {
for loc in locs {
v.visit_local(loc, sc, v);
*loc_pos += 1u;
}
}
decl_item(it) { v.visit_item(it, sc, v); }
}
}
fn visit_arm_with_scope(a: &ast::arm, sc: &scopes, v: &vt[scopes]) {
@ -650,7 +674,9 @@ fn lookup_in_scope(e: &env, sc: scopes, sp: &span, name: &ident,
}
}
}
scope_block(b) { ret lookup_in_block(name, b.node, ns); }
scope_block(b, pos, loc) {
ret lookup_in_block(name, b.node, *pos, *loc, ns);
}
scope_arm(a) {
if ns == ns_value {
ret option::map(ast::def_binding,
@ -755,18 +781,26 @@ fn lookup_in_obj(name: &ident, ob: &ast::_obj, ty_params: &[ast::ty_param],
}
}
fn lookup_in_block(name: &ident, b: &ast::blk_, ns: namespace) ->
option::t[def] {
for st: @ast::stmt in b.stmts {
fn lookup_in_block(name: &ident, b: &ast::blk_, pos: uint, loc_pos: uint,
ns: namespace) -> option::t[def] {
let i = ivec::len(b.stmts);
while i > 0u {
i -= 1u;
let st = b.stmts.(i);
alt st.node {
ast::stmt_decl(d, _) {
alt d.node {
ast::decl_local(locs) {
for loc: @ast::local in locs {
if ns == ns_value {
alt lookup_in_pat(name, loc.node.pat) {
some(did) { ret some(ast::def_local(did)); }
_ {}
if i <= pos {
let j = ivec::len(locs);
while j > 0u {
j -= 1u;
let loc = locs.(j);
if ns == ns_value && (i < pos || j < loc_pos) {
alt lookup_in_pat(name, loc.node.pat) {
some(did) { ret some(ast::def_local(did)); }
_ {}
}
}
}
}
@ -1272,8 +1306,13 @@ fn check_block(e: &@env, b: &ast::blk, x: &(), v: &vt[()]) {
ast::stmt_decl(d, _) {
alt d.node {
ast::decl_local(locs) {
for loc: @ast::local in locs {
check_pat(values, loc.node.pat);
let local_values = checker(*e, "value");
for loc in locs {
for each p in ast::pat_bindings(loc.node.pat) {
let ident = alt p.node { pat_bind(n) { n } };
add_name(local_values, p.span, ident);
check_name(values, p.span, ident);
}
}
}
ast::decl_item(it) {
@ -1339,12 +1378,15 @@ fn checker(e: &env, kind: str) -> checker {
ret @{mutable seen: seen, kind: kind, sess: e.sess};
}
fn add_name(ch: &checker, sp: &span, name: &ident) {
fn check_name(ch: &checker, sp: &span, name: &ident) {
for s: ident in ch.seen {
if str::eq(s, name) {
ch.sess.span_fatal(sp, "duplicate " + ch.kind + " name: " + name);
}
}
}
fn add_name(ch: &checker, sp: &span, name: &ident) {
check_name(ch, sp, name);
ch.seen += ~[name];
}

View File

@ -1,12 +1,10 @@
// error-pattern:Unsatisfied precondition constraint
// error-pattern:unresolved name
// a good test that we merge paths correctly in the presence of a
// variable that's used before it's declared
// should be rejected by typestate because we use x without initializing it
fn my_fail() -> ! { fail; }
fn main() {
alt (true) {
false { my_fail(); }
true {}

View File

@ -3,8 +3,6 @@
// error-pattern: attempted dynamic environment-capture
fn main() {
fn foo() -> int { ret bar; }
let bar: int = 5;
fn foo() -> int { ret bar; }
}

View File

@ -1,3 +0,0 @@
// error-pattern: duplicate value name: x
fn main() { let x = 10; let x: int = 20; }

View File

@ -1,5 +1,6 @@
// error-pattern: Type inference failed because I could not find
fn main() {
let f = @f;
let f;
f = @f;
f();
}

View File

@ -1,4 +1,4 @@
// error-pattern:mismatched types
// From Issue #778
tag clam[T] { a(T); }
fn main() { let c = a(c); alt c { a[int](_) { } } }
fn main() { let c; c = a(c); alt c { a[int](_) { } } }

View File

@ -1,4 +1,5 @@
// error-pattern: Type inference failed because I could not find
fn main() {
let f = @f;
let f;
f = @f;
}

View File

@ -1,5 +1,4 @@
// -*- rust -*-
// error-pattern:src/test/compile-fail/shadow.rs
fn foo(c: [int]) {
let a: int = 5;
let b: [int] = ~[];
@ -7,9 +6,15 @@ fn foo(c: [int]) {
alt none[int] {
some[int](_) { for i: int in c { log a; let a = 17; b += ~[a]; } }
_ {}
}
}
tag t[T] { none; some(T); }
fn main() { foo(~[]); }
fn main() {
let x = 10;
let x = x + 20;
assert x == 30;
foo(~[]);
}