Skip to content

Commit fe6e0ba

Browse files
committed
rustc: Allow constants to be used in patterns
1 parent db9f8db commit fe6e0ba

File tree

8 files changed

+159
-43
lines changed

8 files changed

+159
-43
lines changed

src/librustc/middle/check_alt.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use syntax::ast::*;
22
use syntax::ast_util::{variant_def_ids, dummy_sp, unguarded_pat};
33
use const_eval::{eval_const_expr, const_val, const_int, const_bool,
4-
compare_const_vals};
4+
compare_const_vals, lookup_const_by_id};
55
use syntax::codemap::span;
66
use syntax::print::pprust::pat_to_str;
77
use util::ppaux::ty_to_str;
@@ -229,6 +229,10 @@ fn pat_ctor_id(tcx: ty::ctxt, p: @pat) -> Option<ctor> {
229229
pat_ident(_, _, _) | pat_enum(_, _) => {
230230
match tcx.def_map.find(pat.id) {
231231
Some(def_variant(_, id)) => Some(variant(id)),
232+
Some(def_const(did)) => {
233+
let const_expr = lookup_const_by_id(tcx, did).get();
234+
Some(val(eval_const_expr(tcx, const_expr)))
235+
}
232236
_ => None
233237
}
234238
}
@@ -255,7 +259,7 @@ fn is_wild(tcx: ty::ctxt, p: @pat) -> bool {
255259
pat_wild => { true }
256260
pat_ident(_, _, _) => {
257261
match tcx.def_map.find(pat.id) {
258-
Some(def_variant(_, _)) => { false }
262+
Some(def_variant(_, _)) | Some(def_const(*)) => { false }
259263
_ => { true }
260264
}
261265
}
@@ -344,6 +348,20 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
344348
if variant(id) == ctor_id { Some(vec::tail(r)) }
345349
else { None }
346350
}
351+
Some(def_const(did)) => {
352+
let const_expr = lookup_const_by_id(tcx, did).get();
353+
let e_v = eval_const_expr(tcx, const_expr);
354+
let match_ = match ctor_id {
355+
val(v) => compare_const_vals(e_v, v) == 0,
356+
range(c_lo, c_hi) => {
357+
compare_const_vals(c_lo, e_v) >= 0 &&
358+
compare_const_vals(c_hi, e_v) <= 0
359+
}
360+
single => true,
361+
_ => fail ~"type error"
362+
};
363+
if match_ { Some(vec::tail(r)) } else { None }
364+
}
347365
_ => Some(vec::append(vec::from_elem(arity, wild()), vec::tail(r)))
348366
}
349367
}
@@ -491,6 +509,7 @@ fn is_refutable(tcx: ty::ctxt, pat: &pat) -> bool {
491509
return true;
492510
}
493511
}
512+
Some(def_const(*)) => return true,
494513
_ => ()
495514
}
496515

src/librustc/middle/const_eval.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -149,21 +149,23 @@ fn classify(e: @expr,
149149

150150
fn lookup_const(tcx: ty::ctxt, e: @expr) -> Option<@expr> {
151151
match tcx.def_map.find(e.id) {
152-
Some(ast::def_const(def_id)) => {
153-
if ast_util::is_local(def_id) {
154-
match tcx.items.find(def_id.node) {
155-
None => None,
156-
Some(ast_map::node_item(it, _)) => match it.node {
157-
item_const(_, const_expr) => Some(const_expr),
158-
_ => None
159-
},
160-
Some(_) => None
161-
}
162-
}
163-
else { None }
152+
Some(ast::def_const(def_id)) => lookup_const_by_id(tcx, def_id),
153+
_ => None
154+
}
155+
}
156+
157+
fn lookup_const_by_id(tcx: ty::ctxt, def_id: ast::def_id) -> Option<@expr> {
158+
if ast_util::is_local(def_id) {
159+
match tcx.items.find(def_id.node) {
160+
None => None,
161+
Some(ast_map::node_item(it, _)) => match it.node {
162+
item_const(_, const_expr) => Some(const_expr),
163+
_ => None
164+
},
165+
Some(_) => None
164166
}
165-
Some(_) => None,
166-
None => None
167+
} else {
168+
None
167169
}
168170
}
169171

src/librustc/middle/pat_util.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::map::HashMap;
88

99
export pat_binding_ids, pat_bindings, pat_id_map, PatIdMap;
1010
export pat_is_variant_or_struct, pat_is_binding_or_wild;
11+
export pat_is_const;
1112

1213
type PatIdMap = std::map::HashMap<ident, node_id>;
1314

@@ -33,6 +34,18 @@ fn pat_is_variant_or_struct(dm: resolve::DefMap, pat: @pat) -> bool {
3334
}
3435
}
3536

37+
fn pat_is_const(dm: resolve::DefMap, pat: &pat) -> bool {
38+
match pat.node {
39+
pat_ident(_, _, None) => {
40+
match dm.find(pat.id) {
41+
Some(def_const(*)) => true,
42+
_ => false
43+
}
44+
}
45+
_ => false
46+
}
47+
}
48+
3649
fn pat_is_binding_or_wild(dm: resolve::DefMap, pat: @pat) -> bool {
3750
match pat.node {
3851
pat_ident(*) => !pat_is_variant_or_struct(dm, pat),
@@ -46,7 +59,8 @@ fn pat_bindings(dm: resolve::DefMap, pat: @pat,
4659
do walk_pat(pat) |p| {
4760
match p.node {
4861
pat_ident(binding_mode, pth, _)
49-
if !pat_is_variant_or_struct(dm, p) => {
62+
if !pat_is_variant_or_struct(dm, p) &&
63+
!pat_is_const(dm, p) => {
5064
it(binding_mode, p.id, p.span, pth);
5165
}
5266
_ => {}

src/librustc/middle/resolve.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ impl AllowCapturingSelfFlag : cmp::Eq {
310310

311311
enum BareIdentifierPatternResolution {
312312
FoundStructOrEnumVariant(def),
313-
FoundConst,
313+
FoundConst(def),
314314
BareIdentifierPatternUnresolved
315315
}
316316

@@ -4299,11 +4299,17 @@ impl Resolver {
42994299
self.session
43004300
.str_of(ident)));
43014301
}
4302-
FoundConst => {
4302+
FoundConst(def) if mode == RefutableMode => {
4303+
debug!("(resolving pattern) resolving `%s` to \
4304+
constant",
4305+
self.session.str_of(ident));
4306+
4307+
self.record_def(pattern.id, def);
4308+
}
4309+
FoundConst(_) => {
43034310
self.session.span_err(pattern.span,
4304-
~"pattern variable \
4305-
conflicts with a constant \
4306-
in scope");
4311+
~"only refutable patterns \
4312+
allowed here");
43074313
}
43084314
BareIdentifierPatternUnresolved => {
43094315
debug!("(resolving pattern) binding `%s`",
@@ -4456,8 +4462,8 @@ impl Resolver {
44564462
def @ def_variant(*) | def @ def_class(*) => {
44574463
return FoundStructOrEnumVariant(def);
44584464
}
4459-
def_const(*) => {
4460-
return FoundConst;
4465+
def @ def_const(*) => {
4466+
return FoundConst(def);
44614467
}
44624468
_ => {
44634469
return BareIdentifierPatternUnresolved;

src/librustc/middle/trans/alt.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
158158
// expression.
159159
enum Lit {
160160
UnitLikeStructLit(ast::node_id), // the node ID of the pattern
161-
ExprLit(@ast::expr)
161+
ExprLit(@ast::expr),
162+
ConstLit(ast::def_id), // the def ID of the constant
162163
}
163164

164165
// An option identifying a branch (either a literal, a enum variant or a
@@ -168,11 +169,43 @@ enum Opt {
168169
var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}),
169170
range(@ast::expr, @ast::expr)
170171
}
172+
171173
fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
172174
match (*a, *b) {
173-
(lit(ExprLit(a)), lit(ExprLit(b))) =>
174-
const_eval::compare_lit_exprs(tcx, a, b) == 0,
175-
(lit(UnitLikeStructLit(a)), lit(UnitLikeStructLit(b))) => a == b,
175+
(lit(a), lit(b)) => {
176+
match (a, b) {
177+
(UnitLikeStructLit(a), UnitLikeStructLit(b)) => a == b,
178+
_ => {
179+
let a_expr;
180+
match a {
181+
ExprLit(existing_a_expr) => a_expr = existing_a_expr,
182+
ConstLit(a_const) => {
183+
let e = const_eval::lookup_const_by_id(tcx, a_const);
184+
a_expr = e.get();
185+
}
186+
UnitLikeStructLit(_) => {
187+
fail ~"UnitLikeStructLit should have been handled \
188+
above"
189+
}
190+
}
191+
192+
let b_expr;
193+
match b {
194+
ExprLit(existing_b_expr) => b_expr = existing_b_expr,
195+
ConstLit(b_const) => {
196+
let e = const_eval::lookup_const_by_id(tcx, b_const);
197+
b_expr = e.get();
198+
}
199+
UnitLikeStructLit(_) => {
200+
fail ~"UnitLikeStructLit should have been handled \
201+
above"
202+
}
203+
}
204+
205+
const_eval::compare_lit_exprs(tcx, a_expr, b_expr) == 0
206+
}
207+
}
208+
}
176209
(range(a1, a2), range(b1, b2)) => {
177210
const_eval::compare_lit_exprs(tcx, a1, b1) == 0 &&
178211
const_eval::compare_lit_exprs(tcx, a2, b2) == 0
@@ -200,6 +233,10 @@ fn trans_opt(bcx: block, o: &Opt) -> opt_result {
200233
let datumblock = datum::scratch_datum(bcx, struct_ty, true);
201234
return single_result(datumblock.to_result(bcx));
202235
}
236+
lit(ConstLit(lit_id)) => {
237+
let llval = consts::get_const_val(bcx.ccx(), lit_id);
238+
return single_result(rslt(bcx, llval));
239+
}
203240
var(disr_val, _) => {
204241
return single_result(rslt(bcx, C_int(ccx, disr_val)));
205242
}
@@ -353,7 +390,8 @@ fn enter_match(bcx: block, dm: DefMap, m: &[@Match/&r],
353390
let self = br.pats[col];
354391
match self.node {
355392
ast::pat_ident(_, path, None) => {
356-
if !pat_is_variant_or_struct(dm, self) {
393+
if !pat_is_variant_or_struct(dm, self) &&
394+
!pat_is_const(dm, self) {
357395
let binding_info =
358396
br.data.bindings_map.get(path_to_ident(path));
359397
Store(bcx, val, binding_info.llmatch);
@@ -389,7 +427,8 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r],
389427
ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) |
390428
ast::pat_struct(*) => Some(~[]),
391429
ast::pat_ident(_, _, None)
392-
if !pat_is_variant_or_struct(dm, p) => Some(~[]),
430+
if !pat_is_variant_or_struct(dm, p) &&
431+
!pat_is_const(dm, p) => Some(~[]),
393432
_ => None
394433
}
395434
}
@@ -451,6 +490,15 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
451490
None
452491
}
453492
}
493+
ast::pat_ident(_, _, None) if pat_is_const(tcx.def_map, p) => {
494+
let const_def = tcx.def_map.get(p.id);
495+
let const_def_id = ast_util::def_id_of_def(const_def);
496+
if opt_eq(tcx, &lit(ConstLit(const_def_id)), opt) {
497+
Some(~[])
498+
} else {
499+
None
500+
}
501+
}
454502
ast::pat_lit(l) => {
455503
if opt_eq(tcx, &lit(ExprLit(l)), opt) {Some(~[])} else {None}
456504
}
@@ -675,6 +723,10 @@ fn get_options(ccx: @crate_ctxt, m: &[@Match], col: uint) -> ~[Opt] {
675723
add_to_set(ccx.tcx, &found,
676724
lit(UnitLikeStructLit(cur.id)));
677725
}
726+
Some(ast::def_const(const_did)) => {
727+
add_to_set(ccx.tcx, &found,
728+
lit(ConstLit(const_did)));
729+
}
678730
_ => {}
679731
}
680732
}

src/librustc/middle/trans/consts.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,22 @@ fn const_autoderef(cx: @crate_ctxt, ty: ty::t, v: ValueRef)
9595
}
9696
}
9797

98+
fn get_const_val(cx: @crate_ctxt, def_id: ast::def_id) -> ValueRef {
99+
if !ast_util::is_local(def_id) {
100+
cx.tcx.sess.bug(~"cross-crate constants");
101+
}
102+
if !cx.const_values.contains_key(def_id.node) {
103+
match cx.tcx.items.get(def_id.node) {
104+
ast_map::node_item(@{
105+
node: ast::item_const(_, subexpr), _
106+
}, _) => {
107+
trans_const(cx, subexpr, def_id.node);
108+
}
109+
_ => cx.tcx.sess.bug(~"expected a const to be an item")
110+
}
111+
}
112+
cx.const_values.get(def_id.node)
113+
}
98114

99115
fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
100116
let _icx = cx.insn_ctxt("const_expr");
@@ -359,18 +375,7 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
359375
C_struct(~[f, C_null(T_opaque_box_ptr(cx))])
360376
}
361377
Some(ast::def_const(def_id)) => {
362-
assert ast_util::is_local(def_id);
363-
if ! cx.const_values.contains_key(def_id.node) {
364-
match cx.tcx.items.get(def_id.node) {
365-
ast_map::node_item(@{
366-
node: ast::item_const(_, subexpr), _
367-
}, _) => {
368-
trans_const(cx, subexpr, def_id.node);
369-
}
370-
_ => cx.sess.span_bug(e.span, ~"expected item")
371-
}
372-
}
373-
cx.const_values.get(def_id.node)
378+
get_const_val(cx, def_id)
374379
}
375380
_ => cx.sess.span_bug(e.span, ~"expected a const or fn def")
376381
}

src/librustc/middle/typeck/check/alt.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use syntax::print::pprust;
22
use syntax::ast_util::{walk_pat};
3-
use pat_util::{pat_is_variant_or_struct};
3+
use pat_util::{pat_is_const, pat_is_variant_or_struct};
44

55
fn check_alt(fcx: @fn_ctxt,
66
expr: @ast::expr,
@@ -391,6 +391,11 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
391391
}
392392
fcx.write_ty(pat.id, b_ty);
393393
}
394+
ast::pat_ident(*) if pat_is_const(tcx.def_map, pat) => {
395+
let const_did = ast_util::def_id_of_def(tcx.def_map.get(pat.id));
396+
let const_tpt = ty::lookup_item_type(tcx, const_did);
397+
fcx.write_ty(pat.id, const_tpt.ty);
398+
}
394399
ast::pat_ident(bm, name, sub)
395400
if !pat_is_variant_or_struct(tcx.def_map, pat) => {
396401
let vid = lookup_local(fcx, pat.span, pat.id);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const FOO: int = 10;
2+
const BAR: int = 3;
3+
4+
fn main() {
5+
let x: int = 3;
6+
let y = match x {
7+
FOO => 1,
8+
BAR => 2,
9+
_ => 3
10+
};
11+
assert y == 2;
12+
}
13+

0 commit comments

Comments
 (0)