1
Fork 0

rustc: Allow constants to be used in patterns. r=nmatsakis

This commit is contained in:
Patrick Walton 2012-11-12 22:10:15 -08:00
parent cf002e9d32
commit bad62dcb04
10 changed files with 176 additions and 54 deletions

View file

@ -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)
}
}

View file

@ -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,
_ => ()
}

View file

@ -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
}
}

View file

@ -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);
}
_ => {}

View file

@ -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;

View file

@ -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)));
}
_ => {}
}
}

View file

@ -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")
}

View file

@ -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]),

View file

@ -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);

View 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;
}