Skip to content

Let assignability #3959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/librustc/middle/check_alt.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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 }
}
}
Expand Down Expand Up @@ -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)))
}
}
Expand Down Expand Up @@ -491,6 +509,7 @@ fn is_refutable(tcx: ty::ctxt, pat: &pat) -> bool {
return true;
}
}
Some(def_const(*)) => return true,
_ => ()
}

Expand Down
30 changes: 16 additions & 14 deletions src/librustc/middle/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
16 changes: 15 additions & 1 deletion src/librustc/middle/pat_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ident, node_id>;

Expand All @@ -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),
Expand All @@ -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);
}
_ => {}
Expand Down
20 changes: 13 additions & 7 deletions src/librustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ impl AllowCapturingSelfFlag : cmp::Eq {

enum BareIdentifierPatternResolution {
FoundStructOrEnumVariant(def),
FoundConst,
FoundConst(def),
BareIdentifierPatternUnresolved
}

Expand Down Expand Up @@ -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`",
Expand Down Expand Up @@ -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;
Expand Down
64 changes: 58 additions & 6 deletions src/librustc/middle/trans/alt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)));
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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}
}
Expand Down Expand Up @@ -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)));
}
_ => {}
}
}
Expand Down
29 changes: 17 additions & 12 deletions src/librustc/middle/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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")
}
Expand Down
9 changes: 6 additions & 3 deletions src/librustc/middle/typeck/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion src/librustc/middle/typeck/check/alt.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions src/test/run-pass/consts-in-patterns.rs
Original file line number Diff line number Diff line change
@@ -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