diff --git a/src/librustc/middle/check_alt.rs b/src/librustc/middle/check_alt.rs index 1c2899fe0be04..f61cda0586695 100644 --- a/src/librustc/middle/check_alt.rs +++ b/src/librustc/middle/check_alt.rs @@ -1,7 +1,7 @@ use syntax::ast::*; use syntax::ast_util::{variant_def_ids, dummy_sp, unguarded_pat}; use const_eval::{eval_const_expr, const_val, const_int, const_bool, - compare_const_vals}; + compare_const_vals, lookup_const_by_id}; use syntax::codemap::span; use syntax::print::pprust::pat_to_str; use util::ppaux::ty_to_str; @@ -229,6 +229,10 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> Option { pat_ident(_, _, _) | pat_enum(_, _) => { match tcx.def_map.find(pat.id) { Some(def_variant(_, id)) => Some(variant(id)), + Some(def_const(did)) => { + let const_expr = lookup_const_by_id(tcx, did).get(); + Some(val(eval_const_expr(tcx, const_expr))) + } _ => None } } @@ -255,7 +259,7 @@ fn is_wild(tcx: ty::ctxt, p: @pat) -> bool { pat_wild => { true } pat_ident(_, _, _) => { match tcx.def_map.find(pat.id) { - Some(def_variant(_, _)) => { false } + Some(def_variant(_, _)) | Some(def_const(*)) => { false } _ => { true } } } @@ -344,6 +348,20 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint, if variant(id) == ctor_id { Some(vec::tail(r)) } else { None } } + Some(def_const(did)) => { + let const_expr = lookup_const_by_id(tcx, did).get(); + let e_v = eval_const_expr(tcx, const_expr); + let match_ = match ctor_id { + val(v) => compare_const_vals(e_v, v) == 0, + range(c_lo, c_hi) => { + compare_const_vals(c_lo, e_v) >= 0 && + compare_const_vals(c_hi, e_v) <= 0 + } + single => true, + _ => fail ~"type error" + }; + if match_ { Some(vec::tail(r)) } else { None } + } _ => Some(vec::append(vec::from_elem(arity, wild()), vec::tail(r))) } } @@ -491,6 +509,7 @@ fn is_refutable(tcx: ty::ctxt, pat: &pat) -> bool { return true; } } + Some(def_const(*)) => return true, _ => () } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index cbe5430b5be40..14a30023560fd 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -149,21 +149,23 @@ fn classify(e: @expr, fn lookup_const(tcx: ty::ctxt, e: @expr) -> Option<@expr> { match tcx.def_map.find(e.id) { - Some(ast::def_const(def_id)) => { - if ast_util::is_local(def_id) { - match tcx.items.find(def_id.node) { - None => None, - Some(ast_map::node_item(it, _)) => match it.node { - item_const(_, const_expr) => Some(const_expr), - _ => None - }, - Some(_) => None - } - } - else { None } + Some(ast::def_const(def_id)) => lookup_const_by_id(tcx, def_id), + _ => None + } +} + +fn lookup_const_by_id(tcx: ty::ctxt, def_id: ast::def_id) -> Option<@expr> { + if ast_util::is_local(def_id) { + match tcx.items.find(def_id.node) { + None => None, + Some(ast_map::node_item(it, _)) => match it.node { + item_const(_, const_expr) => Some(const_expr), + _ => None + }, + Some(_) => None } - Some(_) => None, - None => None + } else { + None } } diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index 9196a0b22caf8..30fea75361e99 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -8,6 +8,7 @@ use std::map::HashMap; export pat_binding_ids, pat_bindings, pat_id_map, PatIdMap; export pat_is_variant_or_struct, pat_is_binding_or_wild; +export pat_is_const; type PatIdMap = std::map::HashMap; @@ -33,6 +34,18 @@ fn pat_is_variant_or_struct(dm: resolve::DefMap, pat: @pat) -> bool { } } +fn pat_is_const(dm: resolve::DefMap, pat: &pat) -> bool { + match pat.node { + pat_ident(_, _, None) => { + match dm.find(pat.id) { + Some(def_const(*)) => true, + _ => false + } + } + _ => false + } +} + fn pat_is_binding_or_wild(dm: resolve::DefMap, pat: @pat) -> bool { match pat.node { pat_ident(*) => !pat_is_variant_or_struct(dm, pat), @@ -46,7 +59,8 @@ fn pat_bindings(dm: resolve::DefMap, pat: @pat, do walk_pat(pat) |p| { match p.node { pat_ident(binding_mode, pth, _) - if !pat_is_variant_or_struct(dm, p) => { + if !pat_is_variant_or_struct(dm, p) && + !pat_is_const(dm, p) => { it(binding_mode, p.id, p.span, pth); } _ => {} diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index cea670ba335d6..feb880948855c 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -310,7 +310,7 @@ impl AllowCapturingSelfFlag : cmp::Eq { enum BareIdentifierPatternResolution { FoundStructOrEnumVariant(def), - FoundConst, + FoundConst(def), BareIdentifierPatternUnresolved } @@ -4299,11 +4299,17 @@ impl Resolver { self.session .str_of(ident))); } - FoundConst => { + FoundConst(def) if mode == RefutableMode => { + debug!("(resolving pattern) resolving `%s` to \ + constant", + self.session.str_of(ident)); + + self.record_def(pattern.id, def); + } + FoundConst(_) => { self.session.span_err(pattern.span, - ~"pattern variable \ - conflicts with a constant \ - in scope"); + ~"only refutable patterns \ + allowed here"); } BareIdentifierPatternUnresolved => { debug!("(resolving pattern) binding `%s`", @@ -4456,8 +4462,8 @@ impl Resolver { def @ def_variant(*) | def @ def_class(*) => { return FoundStructOrEnumVariant(def); } - def_const(*) => { - return FoundConst; + def @ def_const(*) => { + return FoundConst(def); } _ => { return BareIdentifierPatternUnresolved; diff --git a/src/librustc/middle/trans/alt.rs b/src/librustc/middle/trans/alt.rs index d3a69a889478b..f289b8d7c1518 100644 --- a/src/librustc/middle/trans/alt.rs +++ b/src/librustc/middle/trans/alt.rs @@ -158,7 +158,8 @@ fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. // expression. enum Lit { UnitLikeStructLit(ast::node_id), // the node ID of the pattern - ExprLit(@ast::expr) + ExprLit(@ast::expr), + ConstLit(ast::def_id), // the def ID of the constant } // An option identifying a branch (either a literal, a enum variant or a @@ -168,11 +169,43 @@ enum Opt { var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}), range(@ast::expr, @ast::expr) } + fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool { match (*a, *b) { - (lit(ExprLit(a)), lit(ExprLit(b))) => - const_eval::compare_lit_exprs(tcx, a, b) == 0, - (lit(UnitLikeStructLit(a)), lit(UnitLikeStructLit(b))) => a == b, + (lit(a), lit(b)) => { + match (a, b) { + (UnitLikeStructLit(a), UnitLikeStructLit(b)) => a == b, + _ => { + let a_expr; + match a { + ExprLit(existing_a_expr) => a_expr = existing_a_expr, + ConstLit(a_const) => { + let e = const_eval::lookup_const_by_id(tcx, a_const); + a_expr = e.get(); + } + UnitLikeStructLit(_) => { + fail ~"UnitLikeStructLit should have been handled \ + above" + } + } + + let b_expr; + match b { + ExprLit(existing_b_expr) => b_expr = existing_b_expr, + ConstLit(b_const) => { + let e = const_eval::lookup_const_by_id(tcx, b_const); + b_expr = e.get(); + } + UnitLikeStructLit(_) => { + fail ~"UnitLikeStructLit should have been handled \ + above" + } + } + + const_eval::compare_lit_exprs(tcx, a_expr, b_expr) == 0 + } + } + } (range(a1, a2), range(b1, b2)) => { const_eval::compare_lit_exprs(tcx, a1, b1) == 0 && const_eval::compare_lit_exprs(tcx, a2, b2) == 0 @@ -200,6 +233,10 @@ fn trans_opt(bcx: block, o: &Opt) -> opt_result { let datumblock = datum::scratch_datum(bcx, struct_ty, true); return single_result(datumblock.to_result(bcx)); } + lit(ConstLit(lit_id)) => { + let llval = consts::get_const_val(bcx.ccx(), lit_id); + return single_result(rslt(bcx, llval)); + } var(disr_val, _) => { return single_result(rslt(bcx, C_int(ccx, disr_val))); } @@ -353,7 +390,8 @@ fn enter_match(bcx: block, dm: DefMap, m: &[@Match/&r], let self = br.pats[col]; match self.node { ast::pat_ident(_, path, None) => { - if !pat_is_variant_or_struct(dm, self) { + if !pat_is_variant_or_struct(dm, self) && + !pat_is_const(dm, self) { let binding_info = br.data.bindings_map.get(path_to_ident(path)); Store(bcx, val, binding_info.llmatch); @@ -389,7 +427,8 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r], ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) | ast::pat_struct(*) => Some(~[]), ast::pat_ident(_, _, None) - if !pat_is_variant_or_struct(dm, p) => Some(~[]), + if !pat_is_variant_or_struct(dm, p) && + !pat_is_const(dm, p) => Some(~[]), _ => None } } @@ -451,6 +490,15 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint, None } } + ast::pat_ident(_, _, None) if pat_is_const(tcx.def_map, p) => { + let const_def = tcx.def_map.get(p.id); + let const_def_id = ast_util::def_id_of_def(const_def); + if opt_eq(tcx, &lit(ConstLit(const_def_id)), opt) { + Some(~[]) + } else { + None + } + } ast::pat_lit(l) => { if opt_eq(tcx, &lit(ExprLit(l)), opt) {Some(~[])} else {None} } @@ -675,6 +723,10 @@ fn get_options(ccx: @crate_ctxt, m: &[@Match], col: uint) -> ~[Opt] { add_to_set(ccx.tcx, &found, lit(UnitLikeStructLit(cur.id))); } + Some(ast::def_const(const_did)) => { + add_to_set(ccx.tcx, &found, + lit(ConstLit(const_did))); + } _ => {} } } diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 6796d139bc76f..dcf5cee952f29 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -95,6 +95,22 @@ fn const_autoderef(cx: @crate_ctxt, ty: ty::t, v: ValueRef) } } +fn get_const_val(cx: @crate_ctxt, def_id: ast::def_id) -> ValueRef { + if !ast_util::is_local(def_id) { + cx.tcx.sess.bug(~"cross-crate constants"); + } + if !cx.const_values.contains_key(def_id.node) { + match cx.tcx.items.get(def_id.node) { + ast_map::node_item(@{ + node: ast::item_const(_, subexpr), _ + }, _) => { + trans_const(cx, subexpr, def_id.node); + } + _ => cx.tcx.sess.bug(~"expected a const to be an item") + } + } + cx.const_values.get(def_id.node) +} fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { let _icx = cx.insn_ctxt("const_expr"); @@ -359,18 +375,7 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) } Some(ast::def_const(def_id)) => { - assert ast_util::is_local(def_id); - if ! cx.const_values.contains_key(def_id.node) { - match cx.tcx.items.get(def_id.node) { - ast_map::node_item(@{ - node: ast::item_const(_, subexpr), _ - }, _) => { - trans_const(cx, subexpr, def_id.node); - } - _ => cx.sess.span_bug(e.span, ~"expected item") - } - } - cx.const_values.get(def_id.node) + get_const_val(cx, def_id) } _ => cx.sess.span_bug(e.span, ~"expected a const or fn def") } diff --git a/src/librustc/middle/typeck/check.rs b/src/librustc/middle/typeck/check.rs index 80cc61a8d074f..f7e92687be6a8 100644 --- a/src/librustc/middle/typeck/check.rs +++ b/src/librustc/middle/typeck/check.rs @@ -1088,10 +1088,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } // A generic function for checking assignment expressions - fn check_assignment(fcx: @fn_ctxt, _sp: span, lhs: @ast::expr, + fn check_assignment(fcx: @fn_ctxt, sp: span, lhs: @ast::expr, rhs: @ast::expr, id: ast::node_id) -> bool { let mut bot = check_expr(fcx, lhs, None); - bot |= check_expr_with(fcx, rhs, fcx.expr_ty(lhs)); + let lhs_type = fcx.expr_ty(lhs); + let unifier = || demand::assign(fcx, sp, lhs_type, rhs); + bot |= check_expr_with_unifier(fcx, rhs, Some(lhs_type), unifier); fcx.write_ty(id, ty::mk_nil(fcx.ccx.tcx)); return bot; } @@ -2247,7 +2249,8 @@ fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) { fn check_decl_initializer(fcx: @fn_ctxt, nid: ast::node_id, init: @ast::expr) -> bool { let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.span, nid)); - return check_expr_with(fcx, init, lty); + let unifier = || demand::assign(fcx, init.span, lty, init); + return check_expr_with_unifier(fcx, init, Some(lty), unifier); } fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool { diff --git a/src/librustc/middle/typeck/check/alt.rs b/src/librustc/middle/typeck/check/alt.rs index d7bee357b66bd..84dcb4d118fe6 100644 --- a/src/librustc/middle/typeck/check/alt.rs +++ b/src/librustc/middle/typeck/check/alt.rs @@ -1,6 +1,6 @@ use syntax::print::pprust; use syntax::ast_util::{walk_pat}; -use pat_util::{pat_is_variant_or_struct}; +use pat_util::{pat_is_const, pat_is_variant_or_struct}; fn check_alt(fcx: @fn_ctxt, expr: @ast::expr, @@ -391,6 +391,11 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { } fcx.write_ty(pat.id, b_ty); } + ast::pat_ident(*) if pat_is_const(tcx.def_map, pat) => { + let const_did = ast_util::def_id_of_def(tcx.def_map.get(pat.id)); + let const_tpt = ty::lookup_item_type(tcx, const_did); + fcx.write_ty(pat.id, const_tpt.ty); + } ast::pat_ident(bm, name, sub) if !pat_is_variant_or_struct(tcx.def_map, pat) => { let vid = lookup_local(fcx, pat.span, pat.id); diff --git a/src/test/run-pass/consts-in-patterns.rs b/src/test/run-pass/consts-in-patterns.rs new file mode 100644 index 0000000000000..9ed1411e88ec1 --- /dev/null +++ b/src/test/run-pass/consts-in-patterns.rs @@ -0,0 +1,13 @@ +const FOO: int = 10; +const BAR: int = 3; + +fn main() { + let x: int = 3; + let y = match x { + FOO => 1, + BAR => 2, + _ => 3 + }; + assert y == 2; +} + diff --git a/src/test/run-pass/let-assignability.rs b/src/test/run-pass/let-assignability.rs new file mode 100644 index 0000000000000..750ebbfac0260 --- /dev/null +++ b/src/test/run-pass/let-assignability.rs @@ -0,0 +1,18 @@ +fn f() { + let a = ~"hello"; + let b: &str = a; + io::println(b); +} + +fn g() { + let c = ~"world"; + let d: &str; + d = c; + io::println(d); +} + +fn main() { + f(); + g(); +} +