提交 a6a5c48c 编写于 作者: N Niko Matsakis

make `ref x` bindings produce region ptrs and fix various minor bugs

we now detect inconsistent modes, binding names, and various other errors.
typeck/trans integration is mostly done.

borrowck not so much.

more tests needed.
上级 ecaf9e39
......@@ -148,7 +148,8 @@ enum meta_item_ {
#[auto_serialize]
enum binding_mode {
bind_by_value,
bind_by_ref
bind_by_ref(ast::mutability),
bind_by_implicit_ref
}
#[auto_serialize]
......
......@@ -228,7 +228,9 @@ fn binder_pat(span: span, nm: ast::ident) -> @ast::pat {
let path = @{span: span, global: false, idents: ~[nm],
rp: none, types: ~[]};
@{id: self.next_id(),
node: ast::pat_ident(ast::bind_by_ref, path, none),
node: ast::pat_ident(ast::bind_by_implicit_ref,
path,
none),
span: span}
}
......@@ -834,7 +836,7 @@ fn ser_enum(cx: ext_ctxt, tps: ser_tps_map, e_name: ast::ident,
// Generate pattern var(v1, v2, v3)
|pats| {
if vec::is_empty(pats) {
ast::pat_ident(ast::bind_by_ref,
ast::pat_ident(ast::bind_by_implicit_ref,
cx.path(v_span, ~[v_name]),
none)
} else {
......
......@@ -116,7 +116,7 @@ fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt {
@{node: {is_mutbl: false,
ty: self.ty_infer(),
pat: @{id: self.next_id(),
node: ast::pat_ident(ast::bind_by_ref,
node: ast::pat_ident(ast::bind_by_implicit_ref,
path(ident,
self.empty_span()),
none),
......
......@@ -16,7 +16,8 @@
import dvec::{dvec, extensions};
import vec::{push};
import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
bind_by_ref, bind_by_value, bitand, bitor, bitxor, blk,
bind_by_ref, bind_by_implicit_ref, bind_by_value,
bitand, bitor, bitxor, blk,
blk_check_mode, bound_const, bound_copy, bound_send, bound_trait,
bound_owned, box, by_copy, by_move, by_mutbl_ref, by_ref, by_val,
capture_clause, capture_item, cdir_dir_mod, cdir_src_mod,
......@@ -1718,7 +1719,9 @@ fn parse_pat(refutable: bool) -> @pat {
} else {
subpat = @{
id: self.get_id(),
node: pat_ident(bind_by_ref, fieldpath, none),
node: pat_ident(bind_by_implicit_ref,
fieldpath,
none),
span: mk_sp(lo, hi)
};
}
......@@ -1749,88 +1752,101 @@ fn parse_pat(refutable: bool) -> @pat {
}
}
tok => {
if !is_ident_or_path(tok) ||
self.is_keyword(~"true") || self.is_keyword(~"false") {
if !is_ident_or_path(tok)
|| self.is_keyword(~"true")
|| self.is_keyword(~"false")
{
let val = self.parse_expr_res(RESTRICT_NO_BAR_OP);
if self.eat_keyword(~"to") {
let end = self.parse_expr_res(RESTRICT_NO_BAR_OP);
hi = end.span.hi;
pat = pat_range(val, end);
} else {
hi = val.span.hi;
pat = pat_lit(val);
}
} else if self.eat_keyword(~"ref") {
let mutbl = self.parse_mutability();
pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl));
} else if self.eat_keyword(~"copy") {
pat = self.parse_pat_ident(refutable, bind_by_value);
} else if !is_plain_ident(self.token) {
pat = self.parse_enum_variant(refutable);
} else {
let binding_mode;
if self.eat_keyword(~"ref") {
binding_mode = bind_by_ref;
} else if self.eat_keyword(~"copy") {
binding_mode = bind_by_value;
} else if refutable {
// XXX: Should be bind_by_value, but that's not
// backward compatible.
binding_mode = bind_by_ref;
} else {
binding_mode = bind_by_value;
}
if is_plain_ident(self.token) &&
match self.look_ahead(1) {
token::LPAREN | token::LBRACKET | token::LT => {
false
}
_ => {
true
}
} {
let name = self.parse_value_path();
let sub = if self.eat(token::AT) {
some(self.parse_pat(refutable))
}
else { none };
pat = pat_ident(binding_mode, name, sub);
} else {
let enum_path = self.parse_path_with_tps(true);
hi = enum_path.span.hi;
let mut args: ~[@pat] = ~[];
let mut star_pat = false;
match self.token {
token::LPAREN => match self.look_ahead(1u) {
token::BINOP(token::STAR) => {
// This is a "top constructor only" pat
self.bump(); self.bump();
star_pat = true;
self.expect(token::RPAREN);
}
_ => {
args = self.parse_unspanned_seq(
token::LPAREN, token::RPAREN,
seq_sep_trailing_disallowed(token::COMMA),
|p| p.parse_pat(refutable));
hi = self.span.hi;
}
}
_ => ()
}
// at this point, we're not sure whether it's a enum or a
// bind
if star_pat {
pat = pat_enum(enum_path, none);
}
else if vec::is_empty(args) &&
vec::len(enum_path.idents) == 1u {
pat = pat_ident(binding_mode, enum_path, none);
}
else {
pat = pat_enum(enum_path, some(args));
}
// this is a plain identifier, like `x` or `x(...)`
match self.look_ahead(1) {
token::LPAREN | token::LBRACKET | token::LT => {
pat = self.parse_enum_variant(refutable);
}
_ => {
let binding_mode = if refutable {
// XXX: Should be bind_by_value, but that's not
// backward compatible.
bind_by_implicit_ref
} else {
bind_by_value
};
pat = self.parse_pat_ident(refutable, binding_mode);
}
}
}
hi = self.span.hi;
}
}
return @{id: self.get_id(), node: pat, span: mk_sp(lo, hi)};
}
fn parse_pat_ident(refutable: bool,
binding_mode: ast::binding_mode) -> ast::pat_ {
if !is_plain_ident(self.token) {
self.span_fatal(
copy self.last_span,
~"expected identifier, found path");
}
let name = self.parse_value_path();
let sub = if self.eat(token::AT) {
some(self.parse_pat(refutable))
} else { none };
// just to be friendly, if they write something like
// ref some(i)
// we end up here with ( as the current token. This shortly
// leads to a parse error. Note that if there is no explicit
// binding mode then we do not end up here, because the lookahead
// will direct us over to parse_enum_variant()
if self.token == token::LPAREN {
self.span_fatal(
copy self.last_span,
~"expected identifier, found enum pattern");
}
pat_ident(binding_mode, name, sub)
}
fn parse_enum_variant(refutable: bool) -> ast::pat_ {
let enum_path = self.parse_path_with_tps(true);
match self.token {
token::LPAREN => {
match self.look_ahead(1u) {
token::BINOP(token::STAR) => { // foo(*)
self.expect(token::LPAREN);
self.expect(token::BINOP(token::STAR));
self.expect(token::RPAREN);
pat_enum(enum_path, none)
}
_ => { // foo(a, ..., z)
let args = self.parse_unspanned_seq(
token::LPAREN, token::RPAREN,
seq_sep_trailing_disallowed(token::COMMA),
|p| p.parse_pat(refutable));
pat_enum(enum_path, some(args))
}
}
}
_ => { // option::none
pat_enum(enum_path, some(~[]))
}
}
}
fn parse_local(is_mutbl: bool,
allow_init: bool) -> @local {
let lo = self.span.lo;
......
......@@ -1332,8 +1332,12 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
ast::pat_wild => word(s.s, ~"_"),
ast::pat_ident(binding_mode, path, sub) => {
match binding_mode {
ast::bind_by_ref => word_space(s, ~"ref"),
ast::bind_by_value => ()
ast::bind_by_ref(mutbl) => {
word_nbsp(s, ~"ref");
print_mutability(s, mutbl);
}
ast::bind_by_implicit_ref |
ast::bind_by_value => {}
}
print_path(s, path, true);
match sub {
......
......@@ -264,15 +264,17 @@ fn cat_def(id: ast::node_id,
mutbl:m, ty:expr_ty}
}
ast::def_binding(vid, ast::bind_by_value) => {
// by-value bindings are basically local variables
ast::def_binding(vid, ast::bind_by_value) |
ast::def_binding(vid, ast::bind_by_ref(_)) => {
// by-value/by-ref bindings are local variables
@{id:id, span:span,
cat:cat_local(vid), lp:some(@lp_local(vid)),
mutbl:m_imm, ty:expr_ty}
}
ast::def_binding(pid, ast::bind_by_ref) => {
// bindings are "special" since they are implicit pointers.
ast::def_binding(pid, ast::bind_by_implicit_ref) => {
// implicit-by-ref bindings are "special" since they are
// implicit pointers.
// lookup the mutability for this binding that we found in
// gather_loans when we categorized it
......
......@@ -404,7 +404,7 @@ fn add_class_fields(self: @ir_maps, did: def_id) {
fn visit_local(local: @local, &&self: @ir_maps, vt: vt<@ir_maps>) {
let def_map = self.tcx.def_map;
do pat_util::pat_bindings(def_map, local.node.pat) |p_id, sp, path| {
do pat_util::pat_bindings(def_map, local.node.pat) |_bm, p_id, sp, path| {
debug!{"adding local variable %d", p_id};
let name = ast_util::path_to_ident(path);
(*self).add_live_node_for_node(p_id, lnk_vdef(sp));
......@@ -587,7 +587,7 @@ fn variable_from_def_map(node_id: node_id,
fn pat_bindings(pat: @pat, f: fn(live_node, variable, span)) {
let def_map = self.tcx.def_map;
do pat_util::pat_bindings(def_map, pat) |p_id, sp, _n| {
do pat_util::pat_bindings(def_map, pat) |_bm, p_id, sp, _n| {
let ln = self.live_node(p_id, sp);
let var = self.variable(p_id, sp);
f(ln, var, sp);
......
......@@ -15,7 +15,7 @@
// use the node_id of their namesake in the first pattern.
fn pat_id_map(dm: resolve3::DefMap, pat: @pat) -> pat_id_map {
let map = std::map::box_str_hash();
do pat_bindings(dm, pat) |p_id, _s, n| {
do pat_bindings(dm, pat) |_bm, p_id, _s, n| {
map.insert(path_to_ident(n), p_id);
};
return map;
......@@ -33,11 +33,11 @@ fn pat_is_variant(dm: resolve3::DefMap, pat: @pat) -> bool {
}
fn pat_bindings(dm: resolve3::DefMap, pat: @pat,
it: fn(node_id, span, @path)) {
it: fn(binding_mode, node_id, span, @path)) {
do walk_pat(pat) |p| {
match p.node {
pat_ident(_, pth, _) if !pat_is_variant(dm, p) => {
it(p.id, p.span, pth);
pat_ident(binding_mode, pth, _) if !pat_is_variant(dm, p) => {
it(binding_mode, p.id, p.span, pth);
}
_ => {}
}
......@@ -46,6 +46,6 @@ fn pat_bindings(dm: resolve3::DefMap, pat: @pat,
fn pat_binding_ids(dm: resolve3::DefMap, pat: @pat) -> ~[node_id] {
let mut found = ~[];
pat_bindings(dm, pat, |b_id, _sp, _pt| vec::push(found, b_id) );
pat_bindings(dm, pat, |_bm, b_id, _sp, _pt| vec::push(found, b_id) );
return found;
}
......@@ -5,9 +5,13 @@
import metadata::decoder::{def_like, dl_def, dl_field, dl_impl};
import middle::lang_items::LanguageItems;
import middle::lint::{deny, allow, forbid, level, unused_imports, warn};
import syntax::ast::{_mod, add, arm, bind_by_value, bitand, bitor, bitxor};
import middle::pat_util::{pat_bindings};
import syntax::ast::{_mod, add, arm};
import syntax::ast::{bind_by_ref, bind_by_implicit_ref, bind_by_value};
import syntax::ast::{bitand, bitor, bitxor};
import syntax::ast::{blk, bound_const, bound_copy, bound_owned, bound_send};
import syntax::ast::{bound_trait, capture_clause, class_ctor, class_dtor};
import syntax::ast::{bound_trait, binding_mode,
capture_clause, class_ctor, class_dtor};
import syntax::ast::{class_member, class_method, crate, crate_num, decl_item};
import syntax::ast::{def, def_arg, def_binding, def_class, def_const, def_fn};
import syntax::ast::{def_foreign_mod, def_id, def_local, def_mod};
......@@ -36,7 +40,7 @@
import syntax::ast::{view_item_import, view_item_use, view_path_glob};
import syntax::ast::{view_path_list, view_path_simple};
import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash};
import syntax::ast_util::{walk_pat};
import syntax::ast_util::{walk_pat, path_to_ident};
import syntax::attr::{attr_metas, contains_name};
import syntax::print::pprust::{pat_to_str, path_to_str};
import syntax::codemap::span;
......@@ -52,12 +56,20 @@
import vec::pop;
import std::list::{cons, list, nil};
import std::map::{hashmap, int_hash, str_hash};
import std::map::{hashmap, int_hash, box_str_hash};
import str_eq = str::eq;
// Definition mapping
type DefMap = hashmap<node_id,def>;
struct binding_info {
span: span;
binding_mode: binding_mode;
}
// Map from the name in a pattern to its binding mode.
type BindingMap = hashmap<ident,binding_info>;
// Implementation resolution
// XXX: This kind of duplicates information kept in ty::method. Maybe it
......@@ -3584,38 +3596,54 @@ fn resolve_local(local: @local, visitor: ResolveVisitor) {
none, visitor);
}
fn num_bindings(pat: @pat) -> uint {
pat_util::pat_binding_ids(self.def_map, pat).len()
fn binding_mode_map(pat: @pat) -> BindingMap {
let result = box_str_hash();
do pat_bindings(self.def_map, pat) |binding_mode, _id, sp, path| {
let ident = path_to_ident(path);
result.insert(ident,
binding_info {span: sp,
binding_mode: binding_mode});
}
return result;
}
fn warn_var_patterns(arm: arm) {
/*
The idea here is that an arm like:
alpha | beta
where alpha is a variant and beta is an identifier that
might refer to a variant that's not in scope will result
in a confusing error message. Showing that beta actually binds a
new variable might help.
*/
for arm.pats.each |p| {
do pat_util::pat_bindings(self.def_map, p) |_id, sp, pth| {
self.session.span_note(sp, fmt!{"Treating %s as a variable \
binding, because it does not denote any variant in scope",
path_to_str(pth)});
}
};
}
fn check_consistent_bindings(arm: arm) {
if arm.pats.len() == 0 { return; }
let good = self.num_bindings(arm.pats[0]);
for arm.pats.each() |p: @pat| {
if self.num_bindings(p) != good {
self.session.span_err(p.span,
~"inconsistent number of bindings");
self.warn_var_patterns(arm);
break;
};
};
if arm.pats.len() == 0 { return; }
let map_0 = self.binding_mode_map(arm.pats[0]);
for arm.pats.eachi() |i, p: @pat| {
let map_i = self.binding_mode_map(p);
for map_0.each |key, binding_0| {
match map_i.find(key) {
none => {
self.session.span_err(
p.span,
fmt!{"variable `%s` from pattern #1 is \
not bound in pattern #%u",
*key, i + 1});
}
some(binding_i) => {
if binding_0.binding_mode != binding_i.binding_mode {
self.session.span_err(
binding_i.span,
fmt!{"variable `%s` is bound with different \
mode in pattern #%u than in pattern #1",
*key, i + 1});
}
}
}
}
for map_i.each |key, binding| {
if !map_0.contains_key(key) {
self.session.span_err(
binding.span,
fmt!{"variable `%s` from pattern #%u is \
not bound in pattern #1",
*key, i + 1});
}
}
}
}
fn resolve_arm(arm: arm, visitor: ResolveVisitor) {
......
......@@ -662,77 +662,86 @@ enum branch_kind { no_branch, single, switch, compare, }
}
}
struct phi_binding {
pat_id: ast::node_id;
phi_val: ValueRef;
mode: ast::binding_mode;
ty: ty::t;
}
type phi_bindings_list = ~[phi_binding];
// Returns false for unreachable blocks
fn make_phi_bindings(bcx: block, map: ~[exit_node],
ids: pat_util::pat_id_map) -> bool {
fn make_phi_bindings(bcx: block,
map: ~[exit_node],
ids: pat_util::pat_id_map)
-> option<phi_bindings_list> {
let _icx = bcx.insn_ctxt(~"alt::make_phi_bindings");
let our_block = bcx.llbb as uint;
let mut success = true, bcx = bcx;
let mut phi_bindings = ~[];
for ids.each |name, node_id| {
let mut llbbs = ~[];
let mut vals = ~[];
let mut binding = none;
for vec::each(map) |ex| {
if ex.to as uint == our_block {
match assoc(name, ex.bound) {
some(binding) => {
some(b) => {
vec::push(llbbs, ex.from);
vec::push(vals, binding.val);
vec::push(vals, b.val);
binding = some(b);
}
none => ()
}
}
}
if vals.len() > 0u {
let local = Phi(bcx, val_ty(vals[0]), vals, llbbs);
bcx.fcx.lllocals.insert(node_id, local_mem(local));
} else { success = false; }
};
if !success {
Unreachable(bcx);
let binding = match binding {
some(binding) => binding,
none => {
Unreachable(bcx);
return none;
}
};
let phi_val = Phi(bcx, val_ty(vals[0]), vals, llbbs);
vec::push(phi_bindings, phi_binding {
pat_id: node_id,
phi_val: phi_val,
mode: binding.mode,
ty: binding.ty
});
}
return success;
return some(move phi_bindings);
}
// Copies by-value bindings into their homes.
fn copy_by_value_bindings(bcx: block,
exit_node_map: &[exit_node],
pat_ids: pat_util::pat_id_map)
-> block {
fn make_pattern_bindings(bcx: block, phi_bindings: phi_bindings_list)
-> block {
let mut bcx = bcx;
let our_block = bcx.llbb as uint;
for pat_ids.each |name, node_id| {
let bindings = dvec::dvec();
for exit_node_map.each |exit_node| {
if exit_node.to as uint == our_block {
match assoc(name, exit_node.bound) {
none => {}
some(binding) => bindings.push(binding)
}
}
}
if bindings.len() == 0 {
again;
}
let binding = bindings[0];
for phi_bindings.each |binding| {
let phi_val = binding.phi_val;
match binding.mode {
ast::bind_by_ref => {}
ast::bind_by_implicit_ref => {
// use local: phi is a ptr to the value
bcx.fcx.lllocals.insert(binding.pat_id,
local_mem(phi_val));
}
ast::bind_by_ref(_) => {
// use local_imm: ptr is the value
bcx.fcx.lllocals.insert(binding.pat_id,
local_imm(phi_val));
}
ast::bind_by_value => {
let llvalue;
match bcx.fcx.lllocals.get(node_id) {
local_mem(llval) =>
llvalue = llval,
local_imm(_) =>
bcx.sess().bug(~"local_imm unexpected here")
}
// by value: make a new temporary and copy the value out
let lltype = type_of::type_of(bcx.fcx.ccx, binding.ty);
let allocation = alloca(bcx, lltype);
let ty = binding.ty;
bcx = copy_val(bcx, INIT, allocation,
load_if_immediate(bcx, llvalue, ty), ty);
bcx.fcx.lllocals.insert(node_id, local_mem(allocation));
load_if_immediate(bcx, phi_val, ty), ty);
bcx.fcx.lllocals.insert(binding.pat_id,
local_mem(allocation));
add_clean(bcx, allocation, ty);
}
}
......@@ -810,13 +819,16 @@ fn mk_fail(bcx: block, sp: span, msg: ~str,
for vec::each(arms) |a| {
let body_cx = bodies[i];
let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]);
if make_phi_bindings(body_cx, exit_map, id_map) {
let body_cx = copy_by_value_bindings(body_cx, exit_map, id_map);
let arm_dest = dup_for_join(dest);
vec::push(arm_dests, arm_dest);
let mut arm_cx = trans_block(body_cx, a.body, arm_dest);
arm_cx = trans_block_cleanups(arm_cx, body_cx);
vec::push(arm_cxs, arm_cx);
match make_phi_bindings(body_cx, exit_map, id_map) {
none => {}
some(phi_bindings) => {
let body_cx = make_pattern_bindings(body_cx, phi_bindings);
let arm_dest = dup_for_join(dest);
vec::push(arm_dests, arm_dest);
let mut arm_cx = trans_block(body_cx, a.body, arm_dest);
arm_cx = trans_block_cleanups(arm_cx, body_cx);
vec::push(arm_cxs, arm_cx);
}
}
i += 1u;
}
......
......@@ -930,7 +930,7 @@ fn ast_constr_to_sp_constr(tcx: ty::ctxt, args: ~[arg], c: @constr) ->
fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding {
let mut lhs = ~[];
do pat_bindings(tcx.def_map, loc.node.pat) |p_id, _s, name| {
do pat_bindings(tcx.def_map, loc.node.pat) |_bm, p_id, _s, name| {
vec::push(lhs, local_dest({ident: path_to_ident(name), node: p_id}));
};
{lhs: lhs, rhs: loc.node.init}
......
......@@ -156,15 +156,32 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
}
fcx.write_ty(pat.id, b_ty);
}
ast::pat_ident(_, name, sub) if !pat_is_variant(tcx.def_map, pat) => {
ast::pat_ident(bm, name, sub) if !pat_is_variant(tcx.def_map, pat) => {
let vid = lookup_local(fcx, pat.span, pat.id);
let mut typ = ty::mk_var(tcx, vid);
demand::suptype(fcx, pat.span, expected, typ);
match bm {
ast::bind_by_ref(mutbl) => {
// if the binding is like
// ref x | ref const x | ref mut x
// then the type of x is &M T where M is the mutability
// and T is the expected type
let region_var = fcx.infcx.next_region_var_nb();
let mt = {ty: expected, mutbl: mutbl};
let region_ty = ty::mk_rptr(tcx, region_var, mt);
demand::eqtype(fcx, pat.span, region_ty, typ);
}
ast::bind_by_value | ast::bind_by_implicit_ref => {
// otherwise the type of x is the expected type T
demand::eqtype(fcx, pat.span, expected, typ);
}
}
let canon_id = pcx.map.get(ast_util::path_to_ident(name));
if canon_id != pat.id {
let tv_id = lookup_local(fcx, pat.span, canon_id);
let ct = ty::mk_var(tcx, tv_id);
demand::suptype(fcx, pat.span, ct, typ);
demand::eqtype(fcx, pat.span, ct, typ);
}
fcx.write_ty(pat.id, typ);
match sub {
......
......@@ -9,7 +9,7 @@ enum foo {
fn main() {
import bar::{alpha, charlie};
match alpha {
alpha | beta => {} //~ ERROR: inconsistent number of bindings
alpha | beta => {} //~ ERROR variable `beta` from pattern #2 is not bound in pattern #1
charlie => {}
}
}
......@@ -2,6 +2,6 @@ enum foo { alpha, beta(int) }
fn main() {
match alpha {
alpha | beta(i) => {} //~ ERROR inconsistent number of bindings
alpha | beta(i) => {} //~ ERROR variable `i` from pattern #2 is not bound in pattern #1
}
}
fn matcher(x: option<int>) {
alt x {
ref some(i) => {} //~ ERROR expected identifier, found enum pattern
none => {}
}
}
fn main() {}
enum opts {
a(int), b(int), c(int)
}
fn matcher1(x: opts) {
alt x {
a(ref i) | b(copy i) => {} //~ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
c(_) => {}
}
}
fn matcher2(x: opts) {
alt x {
a(ref i) | b(i) => {} //~ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
c(_) => {}
}
}
fn matcher3(x: opts) {
alt x {
a(ref mut i) | b(ref const i) => {} //~ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
c(_) => {}
}
}
fn matcher4(x: opts) {
alt x {
a(ref mut i) | b(ref i) => {} //~ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
c(_) => {}
}
}
fn matcher5(x: opts) {
alt x {
a(ref i) | b(ref i) => {}
c(_) => {}
}
}
fn main() {}
\ No newline at end of file
fn main() {
let y = 1;
alt y {
a | b => {} //~ ERROR variable `a` from pattern #1 is not bound in pattern #2
//~^ ERROR variable `b` from pattern #2 is not bound in pattern #1
}
}
type rec = {
f: int
};
fn destructure(x: &mut rec) {
alt *x {
{f: ref mut f} => *f += 1
}
}
fn main() {
let mut v = {f: 22};
destructure(&mut v);
assert v.f == 23;
}
\ No newline at end of file
fn destructure(x: option<int>) -> int {
alt x {
none => 0,
some(ref v) => *v
}
}
fn main() {
assert destructure(some(22)) == 22;
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册