rustc: Allow constants to be used in patterns. r=nmatsakis
This commit is contained in:
parent
cf002e9d32
commit
bad62dcb04
10 changed files with 176 additions and 54 deletions
|
@ -447,8 +447,7 @@ impl gather_loan_ctxt {
|
|||
alt_id: ast::node_id) {
|
||||
do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
|
||||
match pat.node {
|
||||
ast::pat_ident(bm, _, _)
|
||||
if !self.pat_is_variant_or_struct(pat) => {
|
||||
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
|
||||
match bm {
|
||||
ast::bind_by_value | ast::bind_by_move => {
|
||||
// copying does not borrow anything, so no check
|
||||
|
@ -502,5 +501,9 @@ impl gather_loan_ctxt {
|
|||
fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool {
|
||||
pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
|
||||
}
|
||||
|
||||
fn pat_is_binding(&self, pat: @ast::pat) -> bool {
|
||||
pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ctor> {
|
|||
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,
|
||||
_ => ()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ use syntax::codemap::span;
|
|||
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_variant_or_struct, pat_is_binding, pat_is_binding_or_wild;
|
||||
export pat_is_const;
|
||||
|
||||
type PatIdMap = std::map::HashMap<ident, node_id>;
|
||||
|
||||
|
@ -33,9 +34,31 @@ 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(dm: resolve::DefMap, pat: @pat) -> bool {
|
||||
match pat.node {
|
||||
pat_ident(*) => {
|
||||
!pat_is_variant_or_struct(dm, pat) &&
|
||||
!pat_is_const(dm, pat)
|
||||
}
|
||||
_ => 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),
|
||||
pat_ident(*) => pat_is_binding(dm, pat),
|
||||
pat_wild => true,
|
||||
_ => false
|
||||
}
|
||||
|
@ -45,8 +68,7 @@ fn pat_bindings(dm: resolve::DefMap, pat: @pat,
|
|||
it: fn(binding_mode, node_id, span, @path)) {
|
||||
do walk_pat(pat) |p| {
|
||||
match p.node {
|
||||
pat_ident(binding_mode, pth, _)
|
||||
if !pat_is_variant_or_struct(dm, p) => {
|
||||
pat_ident(binding_mode, pth, _) if pat_is_binding(dm, p) => {
|
||||
it(binding_mode, p.id, p.span, pth);
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -310,7 +310,7 @@ impl AllowCapturingSelfFlag : cmp::Eq {
|
|||
|
||||
enum BareIdentifierPatternResolution {
|
||||
FoundStructOrEnumVariant(def),
|
||||
FoundConst,
|
||||
FoundConst(def),
|
||||
BareIdentifierPatternUnresolved
|
||||
}
|
||||
|
||||
|
@ -4308,11 +4308,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`",
|
||||
|
@ -4465,8 +4471,8 @@ impl Resolver {
|
|||
def @ def_variant(*) | def @ def_class(*) => {
|
||||
return FoundStructOrEnumVariant(def);
|
||||
}
|
||||
def_const(*) => {
|
||||
return FoundConst;
|
||||
def @ def_const(*) => {
|
||||
return FoundConst(def);
|
||||
}
|
||||
_ => {
|
||||
return BareIdentifierPatternUnresolved;
|
||||
|
|
|
@ -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,7 @@ 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_binding(dm, self) {
|
||||
let binding_info =
|
||||
br.data.bindings_map.get(path_to_ident(path));
|
||||
Store(bcx, val, binding_info.llmatch);
|
||||
|
@ -388,8 +425,7 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r],
|
|||
match p.node {
|
||||
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(~[]),
|
||||
ast::pat_ident(_, _, None) if pat_is_binding(dm, p) => Some(~[]),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
@ -451,6 +487,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 +720,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)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -389,8 +389,7 @@ fn check_fn(ccx: @crate_ctxt,
|
|||
let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
|
||||
match p.node {
|
||||
ast::pat_ident(_, path, _)
|
||||
if !pat_util::pat_is_variant_or_struct(fcx.ccx.tcx.def_map,
|
||||
p) => {
|
||||
if pat_util::pat_is_binding(fcx.ccx.tcx.def_map, p) => {
|
||||
assign(p.span, p.id, None);
|
||||
debug!("Pattern binding %s is assigned to %s",
|
||||
tcx.sess.str_of(path.idents[0]),
|
||||
|
|
|
@ -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_binding, pat_is_const, pat_is_variant_or_struct};
|
||||
|
||||
fn check_alt(fcx: @fn_ctxt,
|
||||
expr: @ast::expr,
|
||||
|
@ -74,7 +74,7 @@ fn check_legality_of_move_bindings(fcx: @fn_ctxt,
|
|||
if !any_by_move { return; } // pointless micro-optimization
|
||||
for pats.each |pat| {
|
||||
do walk_pat(*pat) |p| {
|
||||
if !pat_is_variant_or_struct(def_map, p) {
|
||||
if pat_is_binding(def_map, p) {
|
||||
match p.node {
|
||||
ast::pat_ident(ast::bind_by_move, _, sub) => {
|
||||
// check legality of moving out of the enum
|
||||
|
@ -391,8 +391,12 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
|||
}
|
||||
fcx.write_ty(pat.id, b_ty);
|
||||
}
|
||||
ast::pat_ident(bm, name, sub)
|
||||
if !pat_is_variant_or_struct(tcx.def_map, pat) => {
|
||||
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_binding(tcx.def_map, pat) => {
|
||||
let vid = lookup_local(fcx, pat.span, pat.id);
|
||||
let mut typ = ty::mk_var(tcx, vid);
|
||||
|
||||
|
|
13
src/test/run-pass/consts-in-patterns.rs
Normal file
13
src/test/run-pass/consts-in-patterns.rs
Normal file
|
@ -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;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue