From c3bc8fada838c15e09e76a9d5d85438667c1636c Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Sat, 14 Jan 2012 16:05:07 -0800 Subject: [PATCH] Allow omission of the '.' after nullary tag patterns This commit allows patterns like: alt x { some(_) { ... } none { } } without the '.' after none. The parser suspends judgment about whether a bare ident is a tag or a new bound variable; instead, the resolver disambiguates. This means that any code after resolution that pattern-matches on patterns needs to call pat_util::normalize_pat, which consults an environment to do this disambiguation. In addition, local variables are no longer allowed to shadow tag names, so this required changing some code (e.g. renaming variables named "mut", and renaming ast::sub to subtract). The parser currently accepts patterns with and without the '.'. Once the compiler and libraries are changed, it will no longer accept the '.'. --- src/comp/middle/alias.rs | 10 +- src/comp/middle/ast_map.rs | 4 +- src/comp/middle/check_alt.rs | 18 ++- src/comp/middle/debuginfo.rs | 8 +- src/comp/middle/pat_util.rs | 119 ++++++++++++++++++ src/comp/middle/resolve.rs | 64 +++++++--- src/comp/middle/trans.rs | 24 ++-- src/comp/middle/trans_alt.rs | 58 ++++++--- src/comp/middle/tstate/annotate.rs | 4 +- src/comp/middle/tstate/auxiliary.rs | 26 ++-- src/comp/middle/tstate/collect_locals.rs | 10 +- src/comp/middle/tstate/pre_post_conditions.rs | 38 +++--- src/comp/middle/tstate/states.rs | 17 +-- src/comp/middle/ty.rs | 37 +++--- src/comp/middle/typeck.rs | 17 +-- src/comp/rustc.rc | 1 + src/comp/syntax/ast.rs | 14 ++- src/comp/syntax/ast_util.rs | 45 ++----- src/comp/syntax/fold.rs | 12 +- src/comp/syntax/parse/parser.rs | 29 ++++- src/comp/syntax/print/pprust.rs | 8 +- src/comp/syntax/visit.rs | 6 +- src/libstd/rope.rs | 14 +-- src/test/run-pass/alt-bot.rs | 2 +- src/test/run-pass/nullary-or-pattern.rs | 10 ++ 25 files changed, 405 insertions(+), 190 deletions(-) create mode 100644 src/comp/middle/pat_util.rs create mode 100644 src/test/run-pass/nullary-or-pattern.rs diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index e10dec83a678..93239ec116e2 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -9,6 +9,7 @@ import std::list; import option::{some, none, is_none}; import list::list; import driver::session::session; +import pat_util::*; // This is not an alias-analyser (though it would merit from becoming one, or // getting input from one, to be more precise). It is a pass that checks @@ -323,7 +324,7 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope, for a: ast::arm in arms { let new_bs = sc.bs; let root_var = path_def_id(cx, root.ex); - let pat_id_map = ast_util::pat_id_map(a.pats[0]); + let pat_id_map = pat_util::pat_id_map(cx.tcx, a.pats[0]); type info = { id: node_id, mutable unsafe_tys: [unsafe_ty], @@ -588,10 +589,11 @@ fn pattern_roots(tcx: ty::ctxt, mut: option::t, pat: @ast::pat) -> [pattern_root] { fn walk(tcx: ty::ctxt, mut: option::t, pat: @ast::pat, &set: [pattern_root]) { - alt pat.node { + alt normalize_pat(tcx, pat).node { ast::pat_wild. | ast::pat_lit(_) | ast::pat_range(_, _) {} - ast::pat_bind(nm, sub) { - set += [{id: pat.id, name: nm, mut: mut, span: pat.span}]; + ast::pat_ident(nm, sub) { + set += [{id: pat.id, name: path_to_ident(nm), mut: mut, + span: pat.span}]; alt sub { some(p) { walk(tcx, mut, p, set); } _ {} } } ast::pat_tag(_, ps) | ast::pat_tup(ps) { diff --git a/src/comp/middle/ast_map.rs b/src/comp/middle/ast_map.rs index 1af44c812098..de04da490141 100644 --- a/src/comp/middle/ast_map.rs +++ b/src/comp/middle/ast_map.rs @@ -44,14 +44,14 @@ fn map_fn(cx: ctx, _fk: visit::fn_kind, decl: fn_decl, _body: blk, } fn map_local(cx: ctx, loc: @local) { - ast_util::pat_bindings(loc.node.pat) {|p| + pat_util::pat_bindings(loc.node.pat) {|p| cx.map.insert(p.id, node_local(cx.local_id)); cx.local_id += 1u; }; } fn map_arm(cx: ctx, arm: arm) { - ast_util::pat_bindings(arm.pats[0]) {|p| + pat_util::pat_bindings(arm.pats[0]) {|p| cx.map.insert(p.id, node_local(cx.local_id)); cx.local_id += 1u; }; diff --git a/src/comp/middle/check_alt.rs b/src/comp/middle/check_alt.rs index d2e3c15009d8..e32866e57494 100644 --- a/src/comp/middle/check_alt.rs +++ b/src/comp/middle/check_alt.rs @@ -1,6 +1,7 @@ import syntax::ast::*; import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs, lit_expr_eq}; +import pat_util::*; import syntax::visit; import option::{some, none}; import driver::session::session; @@ -16,7 +17,12 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) { fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) { visit::visit_expr(ex, s, v); - alt ex.node { expr_alt(_, arms) { check_arms(tcx, arms); } _ { } } + alt ex.node { + expr_alt(_, arms) { + check_arms(tcx, pat_util::normalize_arms(tcx, arms)); + } + _ { } + } } fn check_arms(tcx: ty::ctxt, arms: [arm]) { @@ -66,8 +72,8 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool { } alt a.node { - pat_bind(_, some(p)) { pattern_supersedes(tcx, p, b) } - pat_wild. | pat_bind(_, none.) { true } + pat_ident(_, some(p)) { pattern_supersedes(tcx, p, b) } + pat_wild. | pat_ident(_, none.) { true } pat_lit(la) { alt b.node { pat_lit(lb) { lit_expr_eq(la, lb) } @@ -132,11 +138,11 @@ fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) { } fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool { - alt pat.node { - pat_box(sub) | pat_uniq(sub) | pat_bind(_, some(sub)) { + alt normalize_pat(tcx, pat).node { + pat_box(sub) | pat_uniq(sub) | pat_ident(_, some(sub)) { is_refutable(tcx, sub) } - pat_wild. | pat_bind(_, none.) { false } + pat_wild. | pat_ident(_, none.) { false } pat_lit(_) { true } pat_rec(fields, _) { for field: field_pat in fields { diff --git a/src/comp/middle/debuginfo.rs b/src/comp/middle/debuginfo.rs index f834fa8b58b8..938fcac52d35 100644 --- a/src/comp/middle/debuginfo.rs +++ b/src/comp/middle/debuginfo.rs @@ -8,6 +8,7 @@ import middle::trans_build::B; import middle::ty; import syntax::{ast, codemap}; import ast::ty; +import pat_util::*; import util::ppaux::ty_to_str; export create_local_var; @@ -629,9 +630,10 @@ fn create_local_var(bcx: @block_ctxt, local: @ast::local) option::none. {} } - let name = alt local.node.pat.node { - ast::pat_bind(ident, _) { ident /*XXX deal w/ optional node binding*/ } - }; + let name = path_to_ident(alt pat_util::normalize_pat(bcx_tcx(bcx), + local.node.pat).node { + ast::pat_ident(ident, _) { ident /*XXX deal w/ optional node binding*/ } + }); let loc = codemap::lookup_char_pos(cx.sess.codemap, local.span.lo); let ty = trans::node_id_type(cx, local.node.id); diff --git a/src/comp/middle/pat_util.rs b/src/comp/middle/pat_util.rs new file mode 100644 index 000000000000..f2d1b6ea6cab --- /dev/null +++ b/src/comp/middle/pat_util.rs @@ -0,0 +1,119 @@ +import syntax::ast::*; +import syntax::ast_util; +import syntax::ast_util::respan; +import syntax::fold; +import syntax::fold::*; + +export normalize_arms; +export normalize_pat; +export normalize_pat_def_map; +export pat_binding_ids; +export pat_bindings; +export pat_id_map; +export path_to_ident; + +fn normalize_pat_def_map(dm: resolve::def_map, p: @pat) -> @pat { + // have to do it the hard way b/c ast fold doesn't pass around + // node IDs. bother. + alt p.node { + pat_wild. { p } + pat_ident(_, none.) { normalize_one(dm, p) } + pat_ident(q, some(r)) { + @{node: pat_ident(q, some(normalize_pat_def_map(dm, r))) + with *p} + } + pat_tag(a_path, subs) { + @{node: pat_tag(a_path, + vec::map(subs, {|p| normalize_pat_def_map(dm, p)})) with *p} + } + pat_rec(field_pats, b) { + @{node: pat_rec(vec::map(field_pats, + {|fp| {pat: normalize_pat_def_map(dm, fp.pat) with fp}}), b) + with *p} + } + pat_tup(subs) { + @{node: pat_tup(vec::map(subs, {|p| normalize_pat_def_map(dm, p)})) + with *p} + } + pat_box(q) { + @{node: pat_box(normalize_pat_def_map(dm, q)) + with *p} + } + pat_uniq(q) { + @{node: pat_uniq(normalize_pat_def_map(dm, q)) + with *p} + } + pat_lit(_) { p } + pat_range(_,_) { p } + } +} + +fn normalize_one(dm: resolve::def_map, p: @pat) -> @pat { + alt dm.find(p.id) { + some(d) { + alt p.node { + pat_ident(tag_path, _) { @{id: p.id, + node: pat_tag(tag_path, []), + span: p.span} } + _ { p } + } + } + none. { p } + } +} + +fn normalize_pat(tcx: ty::ctxt, p: @pat) -> @pat { + normalize_pat_def_map(tcx.def_map, p) +} + +fn normalize_arms(tcx: ty::ctxt, arms:[arm]) -> [arm] { + vec::map(arms, {|a| + {pats: + vec::map(a.pats, {|p| + pat_util::normalize_pat(tcx, p)}) + with a}}) +} + +type pat_id_map = std::map::hashmap; + +// This is used because same-named variables in alternative patterns need to +// use the node_id of their namesake in the first pattern. +fn pat_id_map(tcx: ty::ctxt, pat: @pat) -> pat_id_map { + let map = std::map::new_str_hash::(); + pat_bindings(normalize_pat(tcx, pat)) {|bound| + let name = path_to_ident(alt bound.node + { pat_ident(n, _) { n } }); + map.insert(name, bound.id); + }; + ret map; +} + +// This does *not* normalize. The pattern should be already normalized +// if you want to get a normalized pattern out of it. +// Could return a constrained type in order to express that (future work) +fn pat_bindings(pat: @pat, it: block(@pat)) { + alt pat.node { + pat_ident(_, option::none.) { it(pat); } + pat_ident(_, option::some(sub)) { it(pat); pat_bindings(sub, it); } + pat_tag(_, sub) { for p in sub { pat_bindings(p, it); } } + pat_rec(fields, _) { for f in fields { pat_bindings(f.pat, it); } } + pat_tup(elts) { for elt in elts { pat_bindings(elt, it); } } + pat_box(sub) { pat_bindings(sub, it); } + pat_uniq(sub) { pat_bindings(sub, it); } + pat_wild. | pat_lit(_) | pat_range(_, _) { } + } +} + +fn pat_binding_ids(pat: @pat) -> [node_id] { + let found = []; + pat_bindings(pat) {|b| found += [b.id]; }; + ret found; +} + +fn path_to_ident(p: @path) -> ident { + alt vec::last(p.node.idents) { + none. { // sigh + fail "Malformed path"; } + some(i) { ret i; } + } +} diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 5b9925c2ce5d..6ccc8cc12bea 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -3,6 +3,7 @@ import syntax::{ast, ast_util, codemap}; import syntax::ast::*; import ast::{ident, fn_ident, def, def_id, node_id}; import syntax::ast_util::{local_def, def_id_of_def}; +import pat_util::*; import front::attr; import metadata::{csearch, cstore}; @@ -181,10 +182,12 @@ fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) -> sess: sess}; map_crate(e, crate); resolve_imports(*e); - check_for_collisions(e, *crate); check_exports(e); resolve_names(e, crate); resolve_impls(e, crate); + // check_for_collisions must happen after resolve_names so we + // don't complain if a pattern uses the same nullary tag twice + check_for_collisions(e, *crate); if sess.opts.warn_unused_imports { check_unused_imports(e); } @@ -417,6 +420,20 @@ fn resolve_names(e: @env, c: @ast::crate) { } } } + /* Here we determine whether a given pat_ident binds a new + variable a refers to a nullary tag. */ + ast::pat_ident(p, none.) { + let fnd = lookup_in_scope(*e, sc, p.span, path_to_ident(p), + ns_val(ns_a_tag)); + alt fnd { + some(ast::def_variant(did, vid)) { + e.def_map.insert(pat.id, ast::def_variant(did, vid)); + } + _ { + // Binds a var -- nothing needs to be done + } + } + } _ { } } } @@ -539,30 +556,32 @@ fn visit_expr_with_scope(x: @ast::expr, sc: scopes, v: vt) { } } +// This is only for irrefutable patterns (e.g. ones that appear in a let) +// So if x occurs, and x is already known to be a tag, that's always an error. fn visit_local_with_scope(e: @env, loc: @local, sc:scopes, v:vt) { - // Checks whether the given local has the same name as a tag that's + // Check whether the given local has the same name as a tag that's // in scope // We disallow this, in order to make alt patterns consisting of // a single identifier unambiguous (does the pattern "foo" refer // to tag foo, or is it binding a new name foo?) alt loc.node.pat.node { - pat_bind(an_ident,_) { + pat_ident(an_ident,_) { // Be sure to pass ns_a_tag to lookup_in_scope so that // if this is a name that's being shadowed, we don't die - alt lookup_in_scope(*e, sc, loc.span, an_ident, ns_val(ns_a_tag)) { + alt lookup_in_scope(*e, sc, loc.span, + path_to_ident(an_ident), ns_val(ns_a_tag)) { some(ast::def_variant(tag_id,variant_id)) { // Declaration shadows a tag that's in scope. // That's an error. e.sess.span_err(loc.span, #fmt("Declaration of %s shadows a tag that's in scope", - an_ident)); + path_to_ident(an_ident))); } _ {} } } _ {} } - visit::visit_local(loc, sc, v); } @@ -907,7 +926,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) } scope_loop(local) { if ns == ns_val(ns_any_value) { - alt lookup_in_pat(name, local.node.pat) { + alt lookup_in_pat(e, name, local.node.pat) { some(did) { ret some(ast::def_binding(did)); } _ { } } @@ -918,7 +937,7 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) } scope_arm(a) { if ns == ns_val(ns_any_value) { - alt lookup_in_pat(name, a.pats[0]) { + alt lookup_in_pat(e, name, a.pats[0]) { some(did) { ret some(ast::def_binding(did)); } _ { ret none; } } @@ -997,11 +1016,13 @@ fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param]) ret none::; } -fn lookup_in_pat(name: ident, pat: @ast::pat) -> option::t { +fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option::t { let found = none; - ast_util::pat_bindings(pat) {|bound| - let p_name = alt bound.node { ast::pat_bind(n, _) { n } }; - if str::eq(p_name, name) { found = some(local_def(bound.id)); } + + pat_util::pat_bindings(normalize_pat_def_map(e.def_map, pat)) {|bound| + let p_name = alt bound.node { ast::pat_ident(n, _) { n } }; + if str::eq(path_to_ident(p_name), name) + { found = some(local_def(bound.id)); } }; ret found; } @@ -1041,7 +1062,7 @@ fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, let (style, loc) = locs[j]; if ns == ns_val(ns_any_value) && (i < pos || j < loc_pos) { - alt lookup_in_pat(name, loc.node.pat) { + alt lookup_in_pat(e, name, loc.node.pat) { some(did) { ret some(ast::def_local(did, style)); } @@ -1571,9 +1592,9 @@ fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) { } } -fn check_pat(ch: checker, p: @ast::pat) { - ast_util::pat_bindings(p) {|p| - let ident = alt p.node { pat_bind(n, _) { n } }; +fn check_pat(e: @env, ch: checker, p: @ast::pat) { + pat_util::pat_bindings(normalize_pat_def_map(e.def_map, p)) {|p| + let ident = path_to_ident(alt p.node { pat_ident(n, _) { n } }); add_name(ch, p.span, ident); }; } @@ -1581,13 +1602,13 @@ fn check_pat(ch: checker, p: @ast::pat) { fn check_arm(e: @env, a: ast::arm, &&x: (), v: vt<()>) { visit::visit_arm(a, x, v); let ch0 = checker(*e, "binding"); - check_pat(ch0, a.pats[0]); + check_pat(e, ch0, a.pats[0]); let seen0 = ch0.seen; let i = vec::len(a.pats); while i > 1u { i -= 1u; let ch = checker(*e, "binding"); - check_pat(ch, a.pats[i]); + check_pat(e, ch, a.pats[i]); // Ensure the bindings introduced in this pattern are the same as in // the first pattern. @@ -1620,8 +1641,11 @@ fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) { ast::decl_local(locs) { let local_values = checker(*e, "value"); for (_, loc) in locs { - ast_util::pat_bindings(loc.node.pat) {|p| - let ident = alt p.node { pat_bind(n, _) { n } }; + pat_util::pat_bindings + (normalize_pat_def_map(e.def_map, loc.node.pat)) + {|p| + let ident = path_to_ident(alt p.node + { pat_ident(n, _) { n } }); add_name(local_values, p.span, ident); check_name(values, p.span, ident); }; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 2a029c22b3d5..314385a9f25a 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -26,7 +26,8 @@ import back::{link, abi, upcall}; import syntax::{ast, ast_util, codemap}; import syntax::visit; import syntax::codemap::span; -import syntax::print::pprust::{expr_to_str, stmt_to_str}; +import syntax::print::pprust::{expr_to_str, stmt_to_str, path_to_str}; +import pat_util::*; import visit::vt; import util::common::*; import lib::llvm::{llvm, mk_target_data, mk_type_names}; @@ -775,6 +776,8 @@ fn GEP_tag(cx: @block_ctxt, llblobptr: ValueRef, tag_id: ast::def_id, let true_arg_tys: [ty::t] = []; for aty: ty::t in arg_tys { + // Would be nice to have a way of stating the invariant + // that ty_substs is valid for aty let arg_ty = ty::substitute_type_params(bcx_tcx(cx), ty_substs, aty); true_arg_tys += [arg_ty]; } @@ -2203,7 +2206,7 @@ fn trans_eager_binop(cx: @block_ctxt, op: ast::binop, lhs: ValueRef, if is_float { FAdd(cx, lhs, rhs) } else { Add(cx, lhs, rhs) } } - ast::sub. { + ast::subtract. { if is_float { FSub(cx, lhs, rhs) } else { Sub(cx, lhs, rhs) } } @@ -2641,6 +2644,7 @@ fn trans_local_var(cx: @block_ctxt, def: ast::def) -> local_var_result { alt table.find(id) { some(local_mem(v)) { {val: v, kind: owned} } some(local_imm(v)) { {val: v, kind: owned_imm} } + r { fail("take_local: internal error"); } } } alt def { @@ -2649,9 +2653,11 @@ fn trans_local_var(cx: @block_ctxt, def: ast::def) -> local_var_result { ret { val: cx.fcx.llupvars.get(did.node), kind: owned }; } ast::def_arg(did, _) { + assert (cx.fcx.llargs.contains_key(did.node)); ret take_local(cx.fcx.llargs, did.node); } ast::def_local(did, _) | ast::def_binding(did) { + assert (cx.fcx.lllocals.contains_key(did.node)); ret take_local(cx.fcx.lllocals, did.node); } ast::def_self(did) { @@ -3502,6 +3508,7 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt { ret trans_expr(bcx, ast_util::ternary_to_if(e), dest); } ast::expr_alt(expr, arms) { + // tcx.sess.span_note(e.span, "about to call trans_alt"); ret trans_alt::trans_alt(bcx, expr, arms, dest); } ast::expr_block(blk) { @@ -4205,8 +4212,9 @@ fn alloc_ty(cx: @block_ctxt, t: ty::t) -> result { fn alloc_local(cx: @block_ctxt, local: @ast::local) -> @block_ctxt { let t = node_id_type(bcx_ccx(cx), local.node.id); - let is_simple = alt local.node.pat.node { - ast::pat_bind(_, none.) { true } _ { false } + let p = normalize_pat(bcx_tcx(cx), local.node.pat); + let is_simple = alt p.node { + ast::pat_ident(_, none.) { true } _ { false } }; // Do not allocate space for locals that can be kept immediate. let ccx = bcx_ccx(cx); @@ -4219,10 +4227,10 @@ fn alloc_local(cx: @block_ctxt, local: @ast::local) -> @block_ctxt { } } let r = alloc_ty(cx, t); - alt local.node.pat.node { - ast::pat_bind(ident, none.) { + alt p.node { + ast::pat_ident(pth, none.) { if bcx_ccx(cx).sess.opts.debuginfo { - let _: () = str::as_buf(ident, {|buf| + let _: () = str::as_buf(path_to_ident(pth), {|buf| llvm::LLVMSetValueName(r.val, buf) }); } @@ -4635,7 +4643,7 @@ fn trans_const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { if is_float { llvm::LLVMConstFAdd(te1, te2) } else { llvm::LLVMConstAdd(te1, te2) } } - ast::sub. { + ast::subtract. { if is_float { llvm::LLVMConstFSub(te1, te2) } else { llvm::LLVMConstSub(te1, te2) } } diff --git a/src/comp/middle/trans_alt.rs b/src/comp/middle/trans_alt.rs index a7eb94b92886..5addbebddc1a 100644 --- a/src/comp/middle/trans_alt.rs +++ b/src/comp/middle/trans_alt.rs @@ -1,8 +1,10 @@ import core::{str, vec, option}; import option::{some, none}; +import driver::session::session; import lib::llvm::llvm; import lib::llvm::llvm::{ValueRef, BasicBlockRef}; +import pat_util::*; import trans_build::*; import trans::{new_sub_block_ctxt, new_scope_block_ctxt, load_if_immediate}; import syntax::ast; @@ -10,6 +12,7 @@ import syntax::ast_util; import syntax::ast_util::{dummy_sp}; import syntax::ast::def_id; import syntax::codemap::span; +import syntax::print::pprust::pat_to_str; import trans_common::*; @@ -61,6 +64,7 @@ fn trans_opt(bcx: @block_ctxt, o: opt) -> opt_result { } } +// FIXME: invariant -- pat_id is bound in the def_map? fn variant_opt(ccx: @crate_ctxt, pat_id: ast::node_id) -> opt { let vdef = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat_id)); let variants = ty::tag_variants(ccx.tcx, vdef.tg); @@ -83,13 +87,13 @@ type match_branch = bound: bind_map, data: @{body: BasicBlockRef, guard: option::t<@ast::expr>, - id_map: ast_util::pat_id_map}}; + id_map: pat_id_map}}; type match = [match_branch]; fn has_nested_bindings(m: match, col: uint) -> bool { for br in m { alt br.pats[col].node { - ast::pat_bind(_, some(_)) { ret true; } + ast::pat_ident(_, some(_)) { ret true; } _ {} } } @@ -99,12 +103,13 @@ fn has_nested_bindings(m: match, col: uint) -> bool { fn expand_nested_bindings(m: match, col: uint, val: ValueRef) -> match { let result = []; for br in m { - alt br.pats[col].node { - ast::pat_bind(name, some(inner)) { + alt br.pats[col].node { + ast::pat_ident(name, some(inner)) { let pats = vec::slice(br.pats, 0u, col) + [inner] + vec::slice(br.pats, col + 1u, vec::len(br.pats)); result += [@{pats: pats, - bound: br.bound + [{ident: name, val: val}] + bound: br.bound + [{ident: path_to_ident(name), + val: val}] with *br}]; } _ { result += [br]; } @@ -124,8 +129,9 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match { vec::slice(br.pats, col + 1u, vec::len(br.pats)); let new_br = @{pats: pats, bound: alt br.pats[col].node { - ast::pat_bind(name, none.) { - br.bound + [{ident: name, val: val}] + ast::pat_ident(name, none.) { + br.bound + [{ident: path_to_ident(name), + val: val}] } _ { br.bound } } with *br}; @@ -139,11 +145,11 @@ fn enter_match(m: match, col: uint, val: ValueRef, e: enter_pat) -> match { fn enter_default(m: match, col: uint, val: ValueRef) -> match { fn matches_always(p: @ast::pat) -> bool { - ret alt p.node { - ast::pat_wild. | ast::pat_bind(_, none.) | ast::pat_rec(_, _) | - ast::pat_tup(_) { true } - _ { false } - }; + alt p.node { + ast::pat_wild. | ast::pat_rec(_, _) | + ast::pat_ident(_, none.) | ast::pat_tup(_) { true } + _ { false } + } } fn e(p: @ast::pat) -> option::t<[@ast::pat]> { ret if matches_always(p) { some([]) } else { none }; @@ -257,6 +263,8 @@ fn extract_variant_args(bcx: @block_ctxt, pat_id: ast::node_id, vdefs: {tg: def_id, var: def_id}, val: ValueRef) -> {vals: [ValueRef], bcx: @block_ctxt} { let ccx = bcx.fcx.lcx.ccx, bcx = bcx; + // invariant: + // pat_id must have the same length ty_param_substs as vdefs? let ty_param_substs = ty::node_id_to_type_params(ccx.tcx, pat_id); let blobptr = val; let variants = ty::tag_variants(ccx.tcx, vdefs.tg); @@ -274,6 +282,9 @@ fn extract_variant_args(bcx: @block_ctxt, pat_id: ast::node_id, while i < size { check (valid_variant_index(i, bcx, vdefs_tg, vdefs_var)); let r = + // invariant needed: + // how do we know it even makes sense to pass in ty_param_substs + // here? What if it's [] and the tag type has variables in it? trans::GEP_tag(bcx, blobptr, vdefs_tg, vdefs_var, ty_param_substs, i); bcx = r.bcx; @@ -328,7 +339,7 @@ fn pick_col(m: match) -> uint { fn score(p: @ast::pat) -> uint { alt p.node { ast::pat_lit(_) | ast::pat_tag(_, _) | ast::pat_range(_, _) { 1u } - ast::pat_bind(_, some(p)) { score(p) } + ast::pat_ident(_, some(p)) { score(p) } _ { 0u } } } @@ -582,7 +593,7 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail, // Returns false for unreachable blocks fn make_phi_bindings(bcx: @block_ctxt, map: [exit_node], - ids: ast_util::pat_id_map) -> bool { + ids: pat_util::pat_id_map) -> bool { let our_block = bcx.llbb as uint; let success = true, bcx = bcx; ids.items {|name, node_id| @@ -623,7 +634,7 @@ fn make_phi_bindings(bcx: @block_ctxt, map: [exit_node], ret success; } -fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm], +fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms_: [ast::arm], dest: trans::dest) -> @block_ctxt { let bodies = []; let match: match = []; @@ -633,9 +644,15 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm], let er = trans::trans_temp_expr(alt_cx, expr); if er.bcx.unreachable { ret er.bcx; } + /* + n.b. nothing else in this module should need to normalize, + b/c of this call + */ + let arms = normalize_arms(bcx_tcx(cx), arms_); + for a: ast::arm in arms { let body = new_scope_block_ctxt(er.bcx, "case_body"); - let id_map = ast_util::pat_id_map(a.pats[0]); + let id_map = pat_util::pat_id_map(bcx_tcx(cx), a.pats[0]); bodies += [body]; for p: @ast::pat in a.pats { match += @@ -666,7 +683,8 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm], for a: ast::arm in arms { let body_cx = bodies[i]; if make_phi_bindings(body_cx, exit_map, - ast_util::pat_id_map(a.pats[0])) { + pat_util::pat_id_map(bcx_tcx(cx), + a.pats[0])) { let arm_dest = trans::dup_for_join(dest); arm_dests += [arm_dest]; arm_cxs += [trans::trans_block_dps(body_cx, a.body, arm_dest)]; @@ -684,8 +702,10 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm], fn bind_irrefutable_pat(bcx: @block_ctxt, pat: @ast::pat, val: ValueRef, make_copy: bool) -> @block_ctxt { let ccx = bcx.fcx.lcx.ccx, bcx = bcx; - alt pat.node { - ast::pat_bind(_, inner) { + + // Necessary since bind_irrefutable_pat is called outside trans_alt + alt normalize_pat(bcx_tcx(bcx), pat).node { + ast::pat_ident(_,inner) { if make_copy || ccx.copy_map.contains_key(pat.id) { let ty = ty::node_id_to_monotype(ccx.tcx, pat.id); // FIXME: Could constrain pat_bind to make this diff --git a/src/comp/middle/tstate/annotate.rs b/src/comp/middle/tstate/annotate.rs index d4dbf830a5f3..f44ac06e4c16 100644 --- a/src/comp/middle/tstate/annotate.rs +++ b/src/comp/middle/tstate/annotate.rs @@ -1,12 +1,12 @@ import core::{int, uint}; import syntax::ast::*; -import syntax::ast_util::pat_binding_ids; import syntax::visit; import syntax::codemap::span; import util::common::{log_stmt}; import aux::{num_constraints, get_fn_info, crate_ctxt, add_node}; -import middle::tstate::ann::empty_ann; +import ann::empty_ann; +import pat_util::pat_binding_ids; fn collect_ids_expr(e: @expr, rs: @mutable [node_id]) { *rs += [e.id]; } diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs index 5c0c6c78b3a8..cb592be250c2 100644 --- a/src/comp/middle/tstate/auxiliary.rs +++ b/src/comp/middle/tstate/auxiliary.rs @@ -1,5 +1,6 @@ import core::{vec, int, uint, option}; import option::*; +import pat_util::*; import syntax::ast::*; import syntax::ast_util::*; import syntax::{visit, codemap}; @@ -69,10 +70,10 @@ fn tritv_to_str(fcx: fn_ctxt, v: tritv::t) -> str { for p: norm_constraint in constraints(fcx) { alt tritv_get(v, p.bit_num) { dont_care. { } - t { + tt { s += if comma { ", " } else { comma = true; "" } + - if t == tfalse { "!" } else { "" } + + if tt == tfalse { "!" } else { "" } + constraint_to_str(fcx.ccx.tcx, p.c); } } @@ -313,7 +314,7 @@ fn node_id_to_ts_ann(ccx: crate_ctxt, id: node_id) -> ts_ann { #error("node_id_to_ts_ann: no ts_ann for node_id %d", id); fail; } - some(t) { ret t; } + some(tt) { ret tt; } } } @@ -779,13 +780,6 @@ fn replace(subst: subst, d: pred_args) -> [constr_arg_general_] { ret rslt; } -fn path_to_ident(cx: ty::ctxt, p: @path) -> ident { - alt vec::last(p.node.idents) { - none. { cx.sess.span_fatal(p.span, "Malformed path"); } - some(i) { ret i; } - } -} - tag if_ty { if_check; plain_if; } fn local_node_id_to_def_id_strict(fcx: fn_ctxt, sp: span, i: node_id) -> @@ -1059,18 +1053,20 @@ fn ast_constr_to_sp_constr(tcx: ty::ctxt, args: [arg], c: @constr) -> type binding = {lhs: [inst], rhs: option::t}; -fn local_to_bindings(loc: @local) -> binding { +fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding { let lhs = []; - pat_bindings(loc.node.pat) {|p| - let ident = alt p.node { pat_bind(name, _) { name } }; + pat_bindings(pat_util::normalize_pat(tcx, loc.node.pat)) {|p| + let ident = alt p.node + { pat_ident(name, _) { path_to_ident(name) } }; lhs += [{ident: ident, node: p.id}]; }; {lhs: lhs, rhs: loc.node.init} } -fn locals_to_bindings(locals: [(let_style, @local)]) -> [binding] { +fn locals_to_bindings(tcx: ty::ctxt, + locals: [(let_style, @local)]) -> [binding] { let rslt = []; - for (_, loc) in locals { rslt += [local_to_bindings(loc)]; } + for (_, loc) in locals { rslt += [local_to_bindings(tcx, loc)]; } ret rslt; } diff --git a/src/comp/middle/tstate/collect_locals.rs b/src/comp/middle/tstate/collect_locals.rs index f303e7901894..102451a6985f 100644 --- a/src/comp/middle/tstate/collect_locals.rs +++ b/src/comp/middle/tstate/collect_locals.rs @@ -1,18 +1,20 @@ +import option::*; +import pat_util::*; import syntax::ast::*; import syntax::ast_util::*; -import option::*; import syntax::visit; -import aux::*; import util::common::new_def_hash; import syntax::codemap::span; import syntax::ast_util::respan; import driver::session::session; +import aux::*; type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt}; fn collect_local(loc: @local, cx: ctxt, v: visit::vt) { - pat_bindings(loc.node.pat) {|p| - let ident = alt p.node { pat_bind(id, _) { id } }; + pat_bindings(pat_util::normalize_pat(cx.tcx, loc.node.pat)) {|p| + let ident = alt p.node + { pat_ident(id, _) { path_to_ident(id) } }; log(debug, "collect_local: pushing " + ident);; *cx.cs += [respan(loc.span, ninit(p.id, ident))]; }; diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs index 7d52c5882a3f..57bb03797792 100644 --- a/src/comp/middle/tstate/pre_post_conditions.rs +++ b/src/comp/middle/tstate/pre_post_conditions.rs @@ -8,6 +8,8 @@ import bitvectors::{bit_num, seq_preconds, seq_postconds, intersect_states, relax_precond_block, gen}; import tritv::*; + +import pat_util::*; import syntax::ast::*; import syntax::ast_util::*; import syntax::visit; @@ -16,6 +18,7 @@ import util::common::{new_def_hash, log_expr, field_exprs, import syntax::codemap::span; import driver::session::session; + fn find_pre_post_mod(_m: _mod) -> _mod { #debug("implement find_pre_post_mod!"); fail; @@ -103,8 +106,9 @@ fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk, id: node_id) { find_pre_post_expr(fcx, index); find_pre_post_block(fcx, body); - pat_bindings(l.node.pat) {|p| - let ident = alt p.node { pat_bind(id, _) { id } }; + pat_bindings(normalize_pat(fcx.ccx.tcx, l.node.pat)) {|p| + let ident = alt p.node + { pat_ident(id, _) { path_to_ident(id) } }; let v_init = ninit(p.id, ident); relax_precond_block(fcx, bit_num(fcx, v_init) as node_id, body); // Hack: for-loop index variables are frequently ignored, @@ -197,7 +201,7 @@ fn gen_if_local(fcx: fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id, set_pre_and_post(fcx.ccx, larger_id, p.precondition, p.postcondition); gen(fcx, larger_id, - ninit(d_id.node, path_to_ident(fcx.ccx.tcx, pth))); + ninit(d_id.node, path_to_ident(pth))); } _ { find_pre_post_exprs(fcx, [lhs, rhs], larger_id); } } @@ -232,7 +236,7 @@ fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr, def_local(d_id, _) { let i = bit_num(fcx, - ninit(d_id.node, path_to_ident(fcx.ccx.tcx, p))); + ninit(d_id.node, path_to_ident(p))); require_and_preserve(i, expr_pp(fcx.ccx, lhs)); } _ { } @@ -250,9 +254,9 @@ fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr, alt d1 { some(id1) { let instlhs = - {ident: path_to_ident(fcx.ccx.tcx, p), node: id}; + {ident: path_to_ident(p), node: id}; let instrhs = - {ident: path_to_ident(fcx.ccx.tcx, p1), node: id1}; + {ident: path_to_ident(p1), node: id1}; copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs, ty); } @@ -340,7 +344,7 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { expr_path(p) { let rslt = expr_pp(fcx.ccx, e); clear_pp(rslt); - handle_var(fcx, rslt, e.id, path_to_ident(fcx.ccx.tcx, p)); + handle_var(fcx, rslt, e.id, path_to_ident(p)); } expr_log(_, lvl, arg) { find_pre_post_exprs(fcx, [lvl, arg], e.id); @@ -581,18 +585,19 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) { _ { } } - pat_bindings(alocal.node.pat) {|pat| + pat_bindings(normalize_pat(fcx.ccx.tcx, alocal.node.pat)) + {|pat| /* FIXME: This won't be necessary when typestate works well enough for pat_bindings to return a refinement-typed thing. */ - let ident = alt pat.node { pat_bind(n, _) { n } }; + let ident = alt pat.node + { pat_ident(n, _) { path_to_ident(n) } }; alt p { some(p) { copy_in_postcond(fcx, id, {ident: ident, node: pat.id}, {ident: - path_to_ident(fcx.ccx.tcx, - p), + path_to_ident(p), node: an_init.expr.id}, op_to_oper_ty(an_init.op)); } @@ -612,11 +617,14 @@ fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) { seq_preconds(fcx, [prev_pp, e_pp])); /* Include the LHSs too, since those aren't in the postconds of the RHSs themselves */ - pat_bindings(alocal.node.pat) {|pat| + pat_bindings(normalize_pat(fcx.ccx.tcx, alocal.node.pat)) + {|pat| + // FIXME + // Generalize this pattern? map_if_ident... alt pat.node { - pat_bind(n, _) { - set_in_postcond(bit_num(fcx, ninit(pat.id, n)), - prev_pp); + pat_ident(n, _) { + set_in_postcond(bit_num(fcx, + ninit(pat.id, path_to_ident(n))), prev_pp); } } }; diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs index adfc4411adb0..50208b9dacf2 100644 --- a/src/comp/middle/tstate/states.rs +++ b/src/comp/middle/tstate/states.rs @@ -5,6 +5,7 @@ import aux::*; import tritv::{tritv_clone, tritv_set, ttrue}; import bitvectors::*; +import pat_util::*; import syntax::ast::*; import syntax::ast_util::*; import syntax::codemap::span; @@ -37,7 +38,7 @@ fn handle_move_or_copy(fcx: fn_ctxt, post: poststate, rhs_path: @path, some(rhsid) { // RHS is a local var let instrhs = - {ident: path_to_ident(fcx.ccx.tcx, rhs_path), node: rhsid.node}; + {ident: path_to_ident(rhs_path), node: rhsid.node}; copy_in_poststate(fcx, post, instlhs, instrhs, op_to_oper_ty(init_op)); } @@ -143,9 +144,9 @@ fn find_pre_post_state_two(fcx: fn_ctxt, pres: prestate, lhs: @expr, alt d1 { some(id1) { let instlhs = - {ident: path_to_ident(fcx.ccx.tcx, p), node: id}; + {ident: path_to_ident(p), node: id}; let instrhs = - {ident: path_to_ident(fcx.ccx.tcx, p1), node: id1}; + {ident: path_to_ident(p1), node: id1}; copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs, ty); } @@ -206,8 +207,9 @@ fn find_pre_post_state_loop(fcx: fn_ctxt, pres: prestate, l: @local, // Make sure the index vars are considered initialized // in the body let index_post = tritv_clone(expr_poststate(fcx.ccx, index)); - pat_bindings(l.node.pat) {|p| - let ident = alt p.node { pat_bind(name, _) { name } }; + pat_bindings(pat_util::normalize_pat(fcx.ccx.tcx, l.node.pat)) {|p| + let ident = alt p.node + { pat_ident(name, _) { path_to_ident(name) } }; set_in_poststate_ident(fcx, p.id, ident, index_post); }; @@ -231,7 +233,7 @@ fn gen_if_local(fcx: fn_ctxt, p: poststate, e: @expr) -> bool { alt fcx.ccx.tcx.def_map.find(e.id) { some(def_local(loc, _)) { ret set_in_poststate_ident(fcx, loc.node, - path_to_ident(fcx.ccx.tcx, pth), p); + path_to_ident(pth), p); } _ { ret false; } } @@ -632,7 +634,8 @@ fn find_pre_post_state_stmt(fcx: fn_ctxt, pres: prestate, s: @stmt) -> bool { alt adecl.node { decl_local(alocals) { set_prestate(stmt_ann, pres); - let c_and_p = seq_states(fcx, pres, locals_to_bindings(alocals)); + let c_and_p = seq_states(fcx, pres, + locals_to_bindings(fcx.ccx.tcx, alocals)); /* important to do this in one step to ensure termination (don't want to set changed to true for intermediate changes) */ diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index d44b0ee0072f..59fd826791a3 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -371,6 +371,8 @@ const idx_first_others: uint = 20u; type type_store = interner::interner<@raw_t>; +// substs is a list of actuals that correspond to ty's +// formal parameters type ty_param_substs_opt_and_ty = {substs: option::t<[ty::t]>, ty: ty::t}; type node_type_table = @@ -2215,7 +2217,7 @@ mod unify { ty::ty_box(expected_mt) { alt struct(cx.tcx, actual) { ty::ty_box(actual_mt) { - let (mut, var) = alt unify_mut( + let (mutt, var) = alt unify_mut( expected_mt.mut, actual_mt.mut, variance) { none. { ret ures_err(terr_box_mutability); } some(mv) { mv } @@ -2224,7 +2226,7 @@ mod unify { cx, expected_mt.ty, actual_mt.ty, var); alt result { ures_ok(result_sub) { - let mt = {ty: result_sub, mut: mut}; + let mt = {ty: result_sub, mut: mutt}; ret ures_ok(mk_box(cx.tcx, mt)); } _ { ret result; } @@ -2236,7 +2238,7 @@ mod unify { ty::ty_uniq(expected_mt) { alt struct(cx.tcx, actual) { ty::ty_uniq(actual_mt) { - let (mut, var) = alt unify_mut( + let (mutt, var) = alt unify_mut( expected_mt.mut, actual_mt.mut, variance) { none. { ret ures_err(terr_box_mutability); } some(mv) { mv } @@ -2245,7 +2247,7 @@ mod unify { cx, expected_mt.ty, actual_mt.ty, var); alt result { ures_ok(result_mt) { - let mt = {ty: result_mt, mut: mut}; + let mt = {ty: result_mt, mut: mutt}; ret ures_ok(mk_uniq(cx.tcx, mt)); } _ { ret result; } @@ -2257,7 +2259,7 @@ mod unify { ty::ty_vec(expected_mt) { alt struct(cx.tcx, actual) { ty::ty_vec(actual_mt) { - let (mut, var) = alt unify_mut( + let (mutt, var) = alt unify_mut( expected_mt.mut, actual_mt.mut, variance) { none. { ret ures_err(terr_vec_mutability); } some(mv) { mv } @@ -2266,7 +2268,7 @@ mod unify { cx, expected_mt.ty, actual_mt.ty, var); alt result { ures_ok(result_sub) { - let mt = {ty: result_sub, mut: mut}; + let mt = {ty: result_sub, mut: mutt}; ret ures_ok(mk_vec(cx.tcx, mt)); } _ { ret result; } @@ -2278,7 +2280,7 @@ mod unify { ty::ty_ptr(expected_mt) { alt struct(cx.tcx, actual) { ty::ty_ptr(actual_mt) { - let (mut, var) = alt unify_mut( + let (mutt, var) = alt unify_mut( expected_mt.mut, actual_mt.mut, variance) { none. { ret ures_err(terr_vec_mutability); } some(mv) { mv } @@ -2287,7 +2289,7 @@ mod unify { cx, expected_mt.ty, actual_mt.ty, var); alt result { ures_ok(result_sub) { - let mt = {ty: result_sub, mut: mut}; + let mt = {ty: result_sub, mut: mutt}; ret ures_ok(mk_ptr(cx.tcx, mt)); } _ { ret result; } @@ -2342,7 +2344,7 @@ mod unify { while i < expected_len { let expected_field = expected_fields[i]; let actual_field = actual_fields[i]; - let (mut, var) = alt unify_mut( + let (mutt, var) = alt unify_mut( expected_field.mt.mut, actual_field.mt.mut, variance) { none. { ret ures_err(terr_record_mutability); } @@ -2359,7 +2361,7 @@ mod unify { actual_field.mt.ty, var); alt result { ures_ok(rty) { - let mt = {ty: rty, mut: mut}; + let mt = {ty: rty, mut: mutt}; result_fields += [{mt: mt with expected_field}]; } _ { ret result; } @@ -2579,11 +2581,18 @@ fn type_err_to_str(err: ty::type_err) -> str { // Replaces type parameters in the given type using the given list of // substitions. fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t { - if !type_contains_params(cx, typ) { ret typ; } + + if !type_contains_params(cx, typ) { ret typ; } + // Precondition? idx < vec::len(substs) fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint, _did: def_id) -> t { - // FIXME: bounds check can fail - ret substs[idx]; + if idx < vec::len(*substs) { + ret substs[idx]; + } + else { + fail #fmt("Internal error in substituter (substitute_type_params)\ + %u %u", vec::len(*substs), idx); + } } ret fold_ty(cx, fm_param(bind substituter(cx, @substs, _, _)), typ); } @@ -2729,7 +2738,7 @@ fn is_binopable(cx: ctxt, ty: t, op: ast::binop) -> bool { fn opcat(op: ast::binop) -> int { alt op { ast::add. { opcat_add } - ast::sub. { opcat_sub } + ast::subtract. { opcat_sub } ast::mul. { opcat_mult } ast::div. { opcat_mult } ast::rem. { opcat_mult } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index c7736056a502..d447dfcfce4b 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -6,6 +6,7 @@ import metadata::csearch; import driver::session::session; import util::common::*; import syntax::codemap::span; +import pat_util::*; import middle::ty; import middle::ty::{node_id_to_type, arg, block_ty, expr_ty, field, node_type_table, mk_nil, @@ -1137,8 +1138,8 @@ fn gather_locals(ccx: @crate_ctxt, // Add pattern bindings. let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) { - alt p.node { - ast::pat_bind(_, _) { assign(p.id, none); } + alt normalize_pat(ccx.tcx, p).node { + ast::pat_ident(_, _) { assign(p.id, none); } _ {/* no-op */ } } visit::visit_pat(p, e, v); @@ -1181,9 +1182,9 @@ fn valid_range_bounds(from: @ast::expr, to: @ast::expr) -> bool { // Pattern checking is top-down rather than bottom-up so that bindings get // their types immediately. -fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat, +fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat, expected: ty::t) { - alt pat.node { + alt normalize_pat(fcx.ccx.tcx, pat).node { ast::pat_wild. { alt structure_of(fcx, pat.span, expected) { ty::ty_tag(_, expected_tps) { @@ -1218,11 +1219,11 @@ fn check_pat(fcx: @fn_ctxt, map: ast_util::pat_id_map, pat: @ast::pat, } write::ty_only_fixup(fcx, pat.id, b_ty); } - ast::pat_bind(name, sub) { + ast::pat_ident(name, sub) { let vid = lookup_local(fcx, pat.span, pat.id); let typ = ty::mk_var(fcx.ccx.tcx, vid); typ = demand::simple(fcx, pat.span, expected, typ); - let canon_id = map.get(name); + let canon_id = map.get(path_to_ident(name)); if canon_id != pat.id { let ct = ty::mk_var(fcx.ccx.tcx, @@ -2014,7 +2015,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, // bindings. let pattern_ty = ty::expr_ty(tcx, expr); for arm: ast::arm in arms { - let id_map = ast_util::pat_id_map(arm.pats[0]); + let id_map = pat_util::pat_id_map(tcx, arm.pats[0]); for p: @ast::pat in arm.pats { check_pat(fcx, id_map, p, pattern_ty); } @@ -2360,7 +2361,7 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool { } _ {/* fall through */ } } - let id_map = ast_util::pat_id_map(local.node.pat); + let id_map = pat_util::pat_id_map(fcx.ccx.tcx, local.node.pat); check_pat(fcx, id_map, local.node.pat, t); } } diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index f688be64374b..db36e6adc961 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -39,6 +39,7 @@ mod middle { mod gc; mod debuginfo; mod capture; + mod pat_util; mod tstate { mod ck; diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index b953362b1be8..6419e690c440 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -97,7 +97,17 @@ type field_pat = {ident: ident, pat: @pat}; tag pat_ { pat_wild; - pat_bind(ident, option::t<@pat>); + // A pat_ident may either be a new bound variable, + // or a nullary tag (in which case the second field + // is none). + // In the nullary tag case, the parser can't determine + // which it is. The resolver determines this, and + // records this pattern's node_id in an auxiliary + // set (of "pat_idents that refer to nullary tags") + // After the resolution phase, code should never pattern- + // match on a pat directly! Always call pat_util::normalize_pat -- + // it turns any pat_idents that refer to nullary tags into pat_tags. + pat_ident(@path, option::t<@pat>); pat_tag(@path, [@pat]); pat_rec([field_pat], bool); pat_tup([@pat]); @@ -126,7 +136,7 @@ pure fn is_blockish(p: ast::proto) -> bool { tag binop { add; - sub; + subtract; mul; div; rem; diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index eaac7fa9596a..ed65aa11df9e 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -33,43 +33,10 @@ fn def_id_of_def(d: def) -> def_id { } } -type pat_id_map = std::map::hashmap; - -// This is used because same-named variables in alternative patterns need to -// use the node_id of their namesake in the first pattern. -fn pat_id_map(pat: @pat) -> pat_id_map { - let map = std::map::new_str_hash::(); - pat_bindings(pat) {|bound| - let name = alt bound.node { pat_bind(n, _) { n } }; - map.insert(name, bound.id); - }; - ret map; -} - -// FIXME: could return a constrained type -fn pat_bindings(pat: @pat, it: block(@pat)) { - alt pat.node { - pat_bind(_, option::none.) { it(pat); } - pat_bind(_, option::some(sub)) { it(pat); pat_bindings(sub, it); } - pat_tag(_, sub) { for p in sub { pat_bindings(p, it); } } - pat_rec(fields, _) { for f in fields { pat_bindings(f.pat, it); } } - pat_tup(elts) { for elt in elts { pat_bindings(elt, it); } } - pat_box(sub) { pat_bindings(sub, it); } - pat_uniq(sub) { pat_bindings(sub, it); } - pat_wild. | pat_lit(_) | pat_range(_, _) { } - } -} - -fn pat_binding_ids(pat: @pat) -> [node_id] { - let found = []; - pat_bindings(pat) {|b| found += [b.id]; }; - ret found; -} - fn binop_to_str(op: binop) -> str { alt op { add. { ret "+"; } - sub. { ret "-"; } + subtract. { ret "-"; } mul. { ret "*"; } div. { ret "/"; } rem. { ret "%"; } @@ -262,7 +229,7 @@ fn eval_const_expr(e: @expr) -> const_val { alt (eval_const_expr(a), eval_const_expr(b)) { (const_float(a), const_float(b)) { alt op { - add. { const_float(a + b) } sub. { const_float(a - b) } + add. { const_float(a + b) } subtract. { const_float(a - b) } mul. { const_float(a * b) } div. { const_float(a / b) } rem. { const_float(a % b) } eq. { fromb(a == b) } lt. { fromb(a < b) } le. { fromb(a <= b) } ne. { fromb(a != b) } @@ -271,7 +238,7 @@ fn eval_const_expr(e: @expr) -> const_val { } (const_int(a), const_int(b)) { alt op { - add. { const_int(a + b) } sub. { const_int(a - b) } + add. { const_int(a + b) } subtract. { const_int(a - b) } mul. { const_int(a * b) } div. { const_int(a / b) } rem. { const_int(a % b) } and. | bitand. { const_int(a & b) } or. | bitor. { const_int(a | b) } bitxor. { const_int(a ^ b) } @@ -282,7 +249,7 @@ fn eval_const_expr(e: @expr) -> const_val { } (const_uint(a), const_uint(b)) { alt op { - add. { const_uint(a + b) } sub. { const_uint(a - b) } + add. { const_uint(a + b) } subtract. { const_uint(a - b) } mul. { const_uint(a * b) } div. { const_uint(a / b) } rem. { const_uint(a % b) } and. | bitand. { const_uint(a & b) } or. | bitor. { const_uint(a | b) } bitxor. { const_uint(a ^ b) } @@ -327,6 +294,10 @@ fn lit_eq(a: @lit, b: @lit) -> bool { compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0 } +fn ident_to_path(s: span, i: ident) -> @path { + @respan(s, {global: false, idents: [i], types: []}) +} + // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs index e74e1cd9ba75..a5ce71f8cd50 100644 --- a/src/comp/syntax/fold.rs +++ b/src/comp/syntax/fold.rs @@ -10,6 +10,7 @@ export make_fold; export noop_fold_crate; export noop_fold_item; export noop_fold_expr; +export noop_fold_pat; export noop_fold_mod; export noop_fold_ty; @@ -273,8 +274,8 @@ fn noop_fold_arm(a: arm, fld: ast_fold) -> arm { fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ { ret alt p { pat_wild. { p } - pat_bind(ident, sub) { - pat_bind(fld.fold_ident(ident), option::map(sub, fld.fold_pat)) + pat_ident(pth, sub) { + pat_ident(fld.fold_path(pth), option::map(sub, fld.fold_pat)) } pat_lit(_) { p } pat_tag(pth, pats) { @@ -317,8 +318,8 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { let fold_mac = bind fold_mac_(_, fld); ret alt e { - expr_vec(exprs, mut) { - expr_vec(fld.map_exprs(fld.fold_expr, exprs), mut) + expr_vec(exprs, mutt) { + expr_vec(fld.map_exprs(fld.fold_expr, exprs), mutt) } expr_rec(fields, maybe_expr) { expr_rec(vec::map(fields, fold_field), @@ -390,8 +391,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { } expr_path(pth) { expr_path(fld.fold_path(pth)) } expr_fail(e) { expr_fail(option::map(e, fld.fold_expr)) } - expr_break. { e } - expr_cont. { e } + expr_break. | expr_cont. { e } expr_ret(e) { expr_ret(option::map(e, fld.fold_expr)) } expr_be(e) { expr_be(fld.fold_expr(e)) } expr_log(i, lv, e) { expr_log(i, fld.fold_expr(lv), diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index ef65a0bd8971..b8f55dcba963 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -8,6 +8,7 @@ import token::can_begin_expr; import codemap::span; import util::interner; import ast::{node_id, spanned}; +import ast_util::{mk_sp, ident_to_path}; import front::attr; import lexer::reader; import driver::diagnostic; @@ -1097,7 +1098,7 @@ fn prec_table() -> @[op_spec] { {tok: token::BINOP(token::SLASH), op: ast::div, prec: 11}, {tok: token::BINOP(token::PERCENT), op: ast::rem, prec: 11}, {tok: token::BINOP(token::PLUS), op: ast::add, prec: 10}, - {tok: token::BINOP(token::MINUS), op: ast::sub, prec: 10}, + {tok: token::BINOP(token::MINUS), op: ast::subtract, prec: 10}, {tok: token::BINOP(token::LSL), op: ast::lsl, prec: 9}, {tok: token::BINOP(token::LSR), op: ast::lsr, prec: 9}, {tok: token::BINOP(token::ASR), op: ast::asr, prec: 9}, @@ -1165,7 +1166,7 @@ fn parse_assign_expr(p: parser) -> @ast::expr { let aop = ast::add; alt op { token::PLUS. { aop = ast::add; } - token::MINUS. { aop = ast::sub; } + token::MINUS. { aop = ast::subtract; } token::STAR. { aop = ast::mul; } token::SLASH. { aop = ast::div; } token::PERCENT. { aop = ast::rem; } @@ -1426,7 +1427,11 @@ fn parse_pat(p: parser) -> @ast::pat { break; } + let lo1 = p.last_span.lo; let fieldname = parse_ident(p); + let hi1 = p.last_span.lo; + let fieldpath = ast_util::ident_to_path(ast_util::mk_sp(lo1, hi1), + fieldname); let subpat; if p.token == token::COLON { p.bump(); @@ -1436,7 +1441,7 @@ fn parse_pat(p: parser) -> @ast::pat { p.fatal("found " + fieldname + " in binding position"); } subpat = @{id: p.get_id(), - node: ast::pat_bind(fieldname, none), + node: ast::pat_ident(fieldpath, none), span: ast_util::mk_sp(lo, hi)}; } fields += [{ident: fieldname, pat: subpat}]; @@ -1478,7 +1483,10 @@ fn parse_pat(p: parser) -> @ast::pat { } } else if is_plain_ident(p) && alt p.look_ahead(1u) { - token::DOT. | token::LPAREN. | token::LBRACKET. { + // Take this out once the libraries change + token::DOT. | + token::LPAREN. | token::LBRACKET. | + token::LT. { false } _ { true } @@ -1486,7 +1494,7 @@ fn parse_pat(p: parser) -> @ast::pat { hi = p.span.hi; let name = parse_value_ident(p); let sub = eat(p, token::AT) ? some(parse_pat(p)) : none; - pat = ast::pat_bind(name, sub); + pat = ast::pat_ident(ident_to_path(mk_sp(lo, hi), name), sub); } else { let tag_path = parse_path_and_ty_param_substs(p, true); hi = tag_path.span.hi; @@ -1499,10 +1507,19 @@ fn parse_pat(p: parser) -> @ast::pat { args = a.node; hi = a.span.hi; } + token::LBRACE. { args = []; } + // take this out once the libraries change token::DOT. { args = []; p.bump(); } _ { expect(p, token::LPAREN); fail; } } - pat = ast::pat_tag(tag_path, args); + // at this point, we're not sure whether it's a tag or a bind + if vec::len(args) == 0u && + vec::len(tag_path.node.idents) == 1u { + pat = ast::pat_ident(tag_path, none); + } + else { + pat = ast::pat_tag(tag_path, args); + } } } } diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index 092fb5780b1f..258012c4f490 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -1055,10 +1055,12 @@ fn print_pat(s: ps, &&pat: @ast::pat) { maybe_print_comment(s, pat.span.lo); let ann_node = node_pat(s, pat); s.ann.pre(ann_node); + /* Pat isn't normalized, but the beauty of it + is that it doesn't matter */ alt pat.node { ast::pat_wild. { word(s.s, "_"); } - ast::pat_bind(id, sub) { - word(s.s, id); + ast::pat_ident(path, sub) { + print_path(s, path, true); alt sub { some(p) { word(s.s, "@"); print_pat(s, p); } _ {} @@ -1070,7 +1072,7 @@ fn print_pat(s: ps, &&pat: @ast::pat) { popen(s); commasep(s, inconsistent, args, print_pat); pclose(s); - } else { word(s.s, "."); } + } else { word(s.s, "."); } // FIXME } ast::pat_rec(fields, etc) { word(s.s, "{"); diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs index d4cd956d2f19..a48b949bad1e 100644 --- a/src/comp/syntax/visit.rs +++ b/src/comp/syntax/visit.rs @@ -195,9 +195,13 @@ fn visit_pat(p: @pat, e: E, v: vt) { for f: field_pat in fields { v.visit_pat(f.pat, e, v); } } pat_tup(elts) { for elt in elts { v.visit_pat(elt, e, v); } } - pat_box(inner) | pat_uniq(inner) | pat_bind(_, some(inner)) { + pat_box(inner) | pat_uniq(inner) { v.visit_pat(inner, e, v); } + pat_ident(path, inner) { + visit_path(path, e, v); + option::may(inner, {|subpat| v.visit_pat(subpat, e, v)}); + } _ { } } } diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs index 8071ee728476..55175873f5a4 100644 --- a/src/libstd/rope.rs +++ b/src/libstd/rope.rs @@ -1278,7 +1278,7 @@ mod node { while true { alt(get_current_or_next_leaf(it)) { option::none. { ret option::none; } - option::some(leaf) { + option::some(_) { let next_char = get_next_char_in_leaf(it); alt(next_char) { option::none. { @@ -1301,7 +1301,7 @@ mod node { let next = leaf_iterator::next(it.leaf_iterator); alt(next) { option::none. { ret option::none } - option::some(leaf) { + option::some(_) { it.leaf = next; it.leaf_byte_pos = 0u; ret next; @@ -1314,16 +1314,16 @@ mod node { fn get_next_char_in_leaf(it: t) -> option::t { alt(it.leaf) { option::none. { ret option::none } - option::some(leaf) { - if it.leaf_byte_pos >= leaf.byte_len { + option::some(aleaf) { + if it.leaf_byte_pos >= aleaf.byte_len { //We are actually past the end of the leaf it.leaf = option::none; ret option::none } else { let {ch, next} = - str::char_range_at(*leaf.content, - it.leaf_byte_pos + leaf.byte_offset); - it.leaf_byte_pos = next - leaf.byte_offset; + str::char_range_at(*aleaf.content, + it.leaf_byte_pos + aleaf.byte_offset); + it.leaf_byte_pos = next - aleaf.byte_offset; ret option::some(ch) } } diff --git a/src/test/run-pass/alt-bot.rs b/src/test/run-pass/alt-bot.rs index ca9e04825192..bf5f963e9cc9 100644 --- a/src/test/run-pass/alt-bot.rs +++ b/src/test/run-pass/alt-bot.rs @@ -1,6 +1,6 @@ fn main() { let i: int = - alt some::(3) { none::. { fail } some::(_) { 5 } }; + alt some::(3) { none:: { fail } some::(_) { 5 } }; log(debug, i); } diff --git a/src/test/run-pass/nullary-or-pattern.rs b/src/test/run-pass/nullary-or-pattern.rs new file mode 100644 index 000000000000..8d4609f95d06 --- /dev/null +++ b/src/test/run-pass/nullary-or-pattern.rs @@ -0,0 +1,10 @@ +tag blah { a; b; } + +fn or_alt(q: blah) -> int { + alt q { a. | b. { 42 } } +} + +fn main() { + assert (or_alt(a) == 42); + assert (or_alt(b) == 42); +}