From b2eb88843d1b727551464beea3438e9f0159ebad Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 25 Jul 2014 15:18:19 -0700 Subject: [PATCH] librustc: Disallow mutation and assignment in pattern guards, and modify the CFG for match statements. There were two bugs in issue #14684. One was simply that the borrow check didn't know about the correct CFG for match statements: the pattern must be a predecessor of the guard. This disallows the bad behavior if there are bindings in the pattern. But it isn't enough to prevent the memory safety problem, because of wildcards; thus, this patch introduces a more restrictive rule, which disallows assignments and mutable borrows inside guards outright. I discussed this with Niko and we decided this was the best plan of action. This breaks code that performs mutable borrows in pattern guards. Most commonly, the code looks like this: impl Foo { fn f(&mut self, ...) {} fn g(&mut self, ...) { match bar { Baz if self.f(...) => { ... } _ => { ... } } } } Change this code to not use a guard. For example: impl Foo { fn f(&mut self, ...) {} fn g(&mut self, ...) { match bar { Baz => { if self.f(...) { ... } else { ... } } _ => { ... } } } } Sometimes this can result in code duplication, but often it illustrates a hidden memory safety problem. Closes #14684. [breaking-change] --- src/libfmt_macros/lib.rs | 9 +- src/librustc/middle/cfg/construct.rs | 41 +- src/librustc/middle/check_match.rs | 65 ++- src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustdoc/html/render.rs | 22 +- src/libsyntax/parse/parser.rs | 391 ++++++++++-------- .../compile-fail/borrowck-lend-flow-match.rs | 27 -- .../compile-fail/borrowck-mutate-in-guard.rs | 33 ++ .../graphviz-flowgraph/f07.dot-expected.dot | 24 +- .../graphviz-flowgraph/f13.dot-expected.dot | 40 +- 10 files changed, 383 insertions(+), 271 deletions(-) create mode 100644 src/test/compile-fail/borrowck-mutate-in-guard.rs diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 62547b6669f7a..3350d8f548a86 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -352,8 +352,13 @@ impl<'a> Parser<'a> { None => { let tmp = self.cur.clone(); match self.word() { - word if word.len() > 0 && self.consume('$') => { - CountIsName(word) + word if word.len() > 0 => { + if self.consume('$') { + CountIsName(word) + } else { + self.cur = tmp; + CountImplied + } } _ => { self.cur = tmp; diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 4f6885f05ed16..9cd46067c107b 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -333,32 +333,37 @@ impl<'a> CFGBuilder<'a> { // [discr] // | // v 2 - // [guard1] + // [cond1] // / \ // | \ - // v 3 | - // [pat1] | - // | - // v 4 | - // [body1] v - // | [guard2] - // | / \ - // | [body2] \ - // | | ... - // | | | - // v 5 v v - // [....expr....] + // v 3 \ + // [pat1] \ + // | | + // v 4 | + // [guard1] | + // | | + // | | + // v 5 v + // [body1] [cond2] + // | / \ + // | ... ... + // | | | + // v 6 v v + // [.....expr.....] // let discr_exit = self.expr(discr.clone(), pred); // 1 let expr_exit = self.add_node(expr.id, []); - let mut guard_exit = discr_exit; + let mut cond_exit = discr_exit; for arm in arms.iter() { - guard_exit = self.opt_expr(arm.guard, guard_exit); // 2 + cond_exit = self.add_dummy_node([cond_exit]); // 2 let pats_exit = self.pats_any(arm.pats.as_slice(), - guard_exit); // 3 - let body_exit = self.expr(arm.body.clone(), pats_exit); // 4 - self.add_contained_edge(body_exit, expr_exit); // 5 + cond_exit); // 3 + let guard_exit = self.opt_expr(arm.guard, + pats_exit); // 4 + let body_exit = self.expr(arm.body.clone(), + guard_exit); // 5 + self.add_contained_edge(body_exit, expr_exit); // 6 } expr_exit } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index e458b82f03634..4d10676a5892e 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -11,6 +11,10 @@ use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val}; use middle::const_eval::{const_expr_to_pat, eval_const_expr, lookup_const_by_id}; use middle::def::*; +use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init}; +use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode}; +use middle::expr_use_visitor::{WriteAndRead}; +use middle::mem_categorization::cmt; use middle::pat_util::*; use middle::ty::*; use middle::ty; @@ -143,7 +147,16 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) { arm.pats.as_slice()); } - // Second, check for unreachable arms. + // Second, if there is a guard on each arm, make sure it isn't + // assigning or borrowing anything mutably. + for arm in arms.iter() { + match arm.guard { + Some(guard) => check_for_mutation_in_guard(cx, &*guard), + None => {} + } + } + + // Third, check for unreachable arms. check_arms(cx, arms.as_slice()); // Finally, check if the whole match expression is exhaustive. @@ -903,3 +916,53 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, }); } } + +/// Ensures that a pattern guard doesn't borrow by mutable reference or +/// assign. +fn check_for_mutation_in_guard<'a>(cx: &'a MatchCheckCtxt<'a>, guard: &Expr) { + let mut checker = MutationChecker { + cx: cx, + }; + let mut visitor = ExprUseVisitor::new(&mut checker, checker.cx.tcx); + visitor.walk_expr(guard); +} + +struct MutationChecker<'a> { + cx: &'a MatchCheckCtxt<'a>, +} + +impl<'a> Delegate for MutationChecker<'a> { + fn consume(&mut self, _: NodeId, _: Span, _: cmt, _: ConsumeMode) {} + fn consume_pat(&mut self, _: &Pat, _: cmt, _: ConsumeMode) {} + fn borrow(&mut self, + _: NodeId, + span: Span, + _: cmt, + _: Region, + kind: BorrowKind, + _: LoanCause) { + match kind { + MutBorrow => { + self.cx + .tcx + .sess + .span_err(span, + "cannot mutably borrow in a pattern guard") + } + ImmBorrow | UniqueImmBorrow => {} + } + } + fn decl_without_init(&mut self, _: NodeId, _: Span) {} + fn mutate(&mut self, _: NodeId, span: Span, _: cmt, mode: MutateMode) { + match mode { + JustWrite | WriteAndRead => { + self.cx + .tcx + .sess + .span_err(span, "cannot assign in a pattern guard") + } + Init => {} + } + } +} + diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7995317d49fd1..605811555a168 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -292,7 +292,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { self.walk_expr(expr) } - fn walk_expr(&mut self, expr: &ast::Expr) { + pub fn walk_expr(&mut self, expr: &ast::Expr) { debug!("walk_expr(expr={})", expr.repr(self.tcx())); self.walk_adjustment(expr); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index eed058878e082..45af6debe05b0 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1730,17 +1730,19 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item, } }).peekable(); match s.struct_type { - doctree::Plain if fields.peek().is_some() => { - try!(write!(w, "

Fields

\n")); - for field in fields { - try!(write!(w, "")); + doctree::Plain => { + if fields.peek().is_some() { + try!(write!(w, "

Fields

\n
\ - {stab}{name}", - stab = ConciseStability(&field.stability), - name = field.name.get_ref().as_slice())); - try!(document(w, field)); - try!(write!(w, "
")); + for field in fields { + try!(write!(w, "")); + } + try!(write!(w, "
\ + {stab}{name}", + stab = ConciseStability(&field.stability), + name = field.name.get_ref().as_slice())); + try!(document(w, field)); + try!(write!(w, "
")); } - try!(write!(w, "")); } _ => {} } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 0116518d537a7..878994369d0d4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1954,19 +1954,6 @@ impl<'a> Parser<'a> { token::BINOP(token::OR) | token::OROR => { return self.parse_lambda_expr(); }, - _ if self.eat_keyword(keywords::Proc) => { - let decl = self.parse_proc_decl(); - let body = self.parse_expr(); - let fakeblock = P(ast::Block { - view_items: Vec::new(), - stmts: Vec::new(), - expr: Some(body), - id: ast::DUMMY_NODE_ID, - rules: DefaultBlock, - span: body.span, - }); - return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); - }, // FIXME #13626: Should be able to stick in // token::SELF_KEYWORD_NAME token::IDENT(id @ ast::Ident{ @@ -1978,48 +1965,6 @@ impl<'a> Parser<'a> { ex = ExprPath(path); hi = self.last_span.hi; } - _ if self.eat_keyword(keywords::If) => { - return self.parse_if_expr(); - }, - _ if self.eat_keyword(keywords::For) => { - return self.parse_for_expr(None); - }, - _ if self.eat_keyword(keywords::While) => { - return self.parse_while_expr(); - }, - _ if Parser::token_is_lifetime(&self.token) => { - let lifetime = self.get_lifetime(); - self.bump(); - self.expect(&token::COLON); - if self.eat_keyword(keywords::For) { - return self.parse_for_expr(Some(lifetime)) - } else if self.eat_keyword(keywords::Loop) { - return self.parse_loop_expr(Some(lifetime)) - } else { - self.fatal("expected `for` or `loop` after a label") - } - }, - _ if self.eat_keyword(keywords::Loop) => { - return self.parse_loop_expr(None); - }, - _ if self.eat_keyword(keywords::Continue) => { - let lo = self.span.lo; - let ex = if Parser::token_is_lifetime(&self.token) { - let lifetime = self.get_lifetime(); - self.bump(); - ExprAgain(Some(lifetime)) - } else { - ExprAgain(None) - }; - let hi = self.span.hi; - return self.mk_expr(lo, hi, ex); - }, - _ if self.eat_keyword(keywords::Match) => { - return self.parse_match_expr(); - }, - _ if self.eat_keyword(keywords::Unsafe) => { - return self.parse_block_expr(lo, UnsafeBlock(ast::UserProvided)); - }, token::LBRACKET => { self.bump(); @@ -2057,88 +2002,158 @@ impl<'a> Parser<'a> { } hi = self.last_span.hi; }, - _ if self.eat_keyword(keywords::Return) => { - // RETURN expression - if can_begin_expr(&self.token) { - let e = self.parse_expr(); - hi = e.span.hi; - ex = ExprRet(Some(e)); - } else { ex = ExprRet(None); } - }, - _ if self.eat_keyword(keywords::Break) => { - // BREAK expression + _ => { + if self.eat_keyword(keywords::Proc) { + let decl = self.parse_proc_decl(); + let body = self.parse_expr(); + let fakeblock = P(ast::Block { + view_items: Vec::new(), + stmts: Vec::new(), + expr: Some(body), + id: ast::DUMMY_NODE_ID, + rules: DefaultBlock, + span: body.span, + }); + return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); + } + if self.eat_keyword(keywords::If) { + return self.parse_if_expr(); + } + if self.eat_keyword(keywords::For) { + return self.parse_for_expr(None); + } + if self.eat_keyword(keywords::While) { + return self.parse_while_expr(); + } if Parser::token_is_lifetime(&self.token) { let lifetime = self.get_lifetime(); self.bump(); - ex = ExprBreak(Some(lifetime)); - } else { - ex = ExprBreak(None); + self.expect(&token::COLON); + if self.eat_keyword(keywords::For) { + return self.parse_for_expr(Some(lifetime)) + } + if self.eat_keyword(keywords::Loop) { + return self.parse_loop_expr(Some(lifetime)) + } + self.fatal("expected `for` or `loop` after a label") } - hi = self.span.hi; - }, - _ if self.token == token::MOD_SEP || - is_ident(&self.token) && !self.is_keyword(keywords::True) && - !self.is_keyword(keywords::False) => { - let pth = self.parse_path(LifetimeAndTypesWithColons).path; - - // `!`, as an operator, is prefix, so we know this isn't that - if self.token == token::NOT { - // MACRO INVOCATION expression - self.bump(); - - let ket = token::close_delimiter_for(&self.token) - .unwrap_or_else(|| self.fatal("expected open delimiter")); - self.bump(); - - let tts = self.parse_seq_to_end(&ket, - seq_sep_none(), - |p| p.parse_token_tree()); + if self.eat_keyword(keywords::Loop) { + return self.parse_loop_expr(None); + } + if self.eat_keyword(keywords::Continue) { + let lo = self.span.lo; + let ex = if Parser::token_is_lifetime(&self.token) { + let lifetime = self.get_lifetime(); + self.bump(); + ExprAgain(Some(lifetime)) + } else { + ExprAgain(None) + }; let hi = self.span.hi; + return self.mk_expr(lo, hi, ex); + } + if self.eat_keyword(keywords::Match) { + return self.parse_match_expr(); + } + if self.eat_keyword(keywords::Unsafe) { + return self.parse_block_expr( + lo, + UnsafeBlock(ast::UserProvided)); + } + if self.eat_keyword(keywords::Return) { + // RETURN expression + if can_begin_expr(&self.token) { + let e = self.parse_expr(); + hi = e.span.hi; + ex = ExprRet(Some(e)); + } else { + ex = ExprRet(None); + } + } else if self.eat_keyword(keywords::Break) { + // BREAK expression + if Parser::token_is_lifetime(&self.token) { + let lifetime = self.get_lifetime(); + self.bump(); + ex = ExprBreak(Some(lifetime)); + } else { + ex = ExprBreak(None); + } + hi = self.span.hi; + } else if self.token == token::MOD_SEP || + is_ident(&self.token) && + !self.is_keyword(keywords::True) && + !self.is_keyword(keywords::False) { + let pth = + self.parse_path(LifetimeAndTypesWithColons).path; + + // `!`, as an operator, is prefix, so we know this isn't that + if self.token == token::NOT { + // MACRO INVOCATION expression + self.bump(); - return self.mk_mac_expr(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT)); - } else if self.token == token::LBRACE { - // This is a struct literal, unless we're prohibited from - // parsing struct literals here. - if self.restriction != RESTRICT_NO_STRUCT_LITERAL { - // It's a struct literal. + let ket = token::close_delimiter_for(&self.token) + .unwrap_or_else(|| { + self.fatal("expected open delimiter") + }); self.bump(); - let mut fields = Vec::new(); - let mut base = None; - while self.token != token::RBRACE { - if self.eat(&token::DOTDOT) { - base = Some(self.parse_expr()); - break; + let tts = self.parse_seq_to_end( + &ket, + seq_sep_none(), + |p| p.parse_token_tree()); + let hi = self.span.hi; + + return self.mk_mac_expr(lo, + hi, + MacInvocTT(pth, + tts, + EMPTY_CTXT)); + } + if self.token == token::LBRACE { + // This is a struct literal, unless we're prohibited + // from parsing struct literals here. + if self.restriction != RESTRICT_NO_STRUCT_LITERAL { + // It's a struct literal. + self.bump(); + let mut fields = Vec::new(); + let mut base = None; + + while self.token != token::RBRACE { + if self.eat(&token::DOTDOT) { + base = Some(self.parse_expr()); + break; + } + + fields.push(self.parse_field()); + self.commit_expr(fields.last().unwrap().expr, + &[token::COMMA], + &[token::RBRACE]); } - fields.push(self.parse_field()); - self.commit_expr(fields.last().unwrap().expr, - &[token::COMMA], &[token::RBRACE]); - } + if fields.len() == 0 && base.is_none() { + let last_span = self.last_span; + self.span_err(last_span, + "structure literal must either \ + have at least one field or use \ + functional structure update \ + syntax"); + } - if fields.len() == 0 && base.is_none() { - let last_span = self.last_span; - self.span_err(last_span, - "structure literal must either have at \ - least one field or use functional \ - structure update syntax"); + hi = self.span.hi; + self.expect(&token::RBRACE); + ex = ExprStruct(pth, fields, base); + return self.mk_expr(lo, hi, ex); } - - hi = self.span.hi; - self.expect(&token::RBRACE); - ex = ExprStruct(pth, fields, base); - return self.mk_expr(lo, hi, ex); } - } - hi = pth.span.hi; - ex = ExprPath(pth); - }, - _ => { - // other literal expression - let lit = self.parse_lit(); - hi = lit.span.hi; - ex = ExprLit(box(GC) lit); + hi = pth.span.hi; + ex = ExprPath(pth); + } else { + // other literal expression + let lit = self.parse_lit(); + hi = lit.span.hi; + ex = ExprLit(box(GC) lit); + } } } @@ -2501,37 +2516,41 @@ impl<'a> Parser<'a> { } }; } - token::IDENT(_, _) if self.is_keyword(keywords::Box) => { - self.bump(); + token::IDENT(_, _) => { + if self.is_keyword(keywords::Box) { + self.bump(); - // Check for a place: `box(PLACE) EXPR`. - if self.eat(&token::LPAREN) { - // Support `box() EXPR` as the default. - if !self.eat(&token::RPAREN) { - let place = self.parse_expr(); - self.expect(&token::RPAREN); - let subexpression = self.parse_prefix_expr(); - hi = subexpression.span.hi; - ex = ExprBox(place, subexpression); - return self.mk_expr(lo, hi, ex); + // Check for a place: `box(PLACE) EXPR`. + if self.eat(&token::LPAREN) { + // Support `box() EXPR` as the default. + if !self.eat(&token::RPAREN) { + let place = self.parse_expr(); + self.expect(&token::RPAREN); + let subexpression = self.parse_prefix_expr(); + hi = subexpression.span.hi; + ex = ExprBox(place, subexpression); + return self.mk_expr(lo, hi, ex); + } } - } - // Otherwise, we use the unique pointer default. - let subexpression = self.parse_prefix_expr(); - hi = subexpression.span.hi; - // HACK: turn `box [...]` into a boxed-vec - ex = match subexpression.node { - ExprVec(..) | ExprRepeat(..) => { - let last_span = self.last_span; - self.obsolete(last_span, ObsoleteOwnedVector); - ExprVstore(subexpression, ExprVstoreUniq) - } - ExprLit(lit) if lit_is_str(lit) => { - ExprVstore(subexpression, ExprVstoreUniq) - } - _ => self.mk_unary(UnUniq, subexpression) - }; + // Otherwise, we use the unique pointer default. + let subexpression = self.parse_prefix_expr(); + hi = subexpression.span.hi; + // HACK: turn `box [...]` into a boxed-vec + ex = match subexpression.node { + ExprVec(..) | ExprRepeat(..) => { + let last_span = self.last_span; + self.obsolete(last_span, ObsoleteOwnedVector); + ExprVstore(subexpression, ExprVstoreUniq) + } + ExprLit(lit) if lit_is_str(lit) => { + ExprVstore(subexpression, ExprVstoreUniq) + } + _ => self.mk_unary(UnUniq, subexpression) + }; + } else { + return self.parse_dot_or_call_expr() + } } _ => return self.parse_dot_or_call_expr() } @@ -3832,17 +3851,6 @@ impl<'a> Parser<'a> { } SelfStatic } - token::IDENT(..) if self.is_self_ident() => { - let self_ident = self.expect_self_ident(); - - // Determine whether this is the fully explicit form, `self: - // TYPE`. - if self.eat(&token::COLON) { - SelfExplicit(self.parse_ty(false), self_ident) - } else { - SelfValue(self_ident) - } - } token::BINOP(token::STAR) => { // Possibly "*self" or "*mut self" -- not supported. Try to avoid // emitting cryptic "unexpected token" errors. @@ -3860,30 +3868,47 @@ impl<'a> Parser<'a> { // error case, making bogus self ident: SelfValue(special_idents::self_) } - _ if Parser::token_is_mutability(&self.token) && - self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) => { - mutbl_self = self.parse_mutability(); - let self_ident = self.expect_self_ident(); - - // Determine whether this is the fully explicit form, `self: - // TYPE`. - if self.eat(&token::COLON) { - SelfExplicit(self.parse_ty(false), self_ident) + token::IDENT(..) => { + if self.is_self_ident() { + let self_ident = self.expect_self_ident(); + + // Determine whether this is the fully explicit form, `self: + // TYPE`. + if self.eat(&token::COLON) { + SelfExplicit(self.parse_ty(false), self_ident) + } else { + SelfValue(self_ident) + } + } else if Parser::token_is_mutability(&self.token) && + self.look_ahead(1, |t| { + token::is_keyword(keywords::Self, t) + }) { + mutbl_self = self.parse_mutability(); + let self_ident = self.expect_self_ident(); + + // Determine whether this is the fully explicit form, + // `self: TYPE`. + if self.eat(&token::COLON) { + SelfExplicit(self.parse_ty(false), self_ident) + } else { + SelfValue(self_ident) + } + } else if Parser::token_is_mutability(&self.token) && + self.look_ahead(1, |t| *t == token::TILDE) && + self.look_ahead(2, |t| { + token::is_keyword(keywords::Self, t) + }) { + mutbl_self = self.parse_mutability(); + self.bump(); + drop(self.expect_self_ident()); + let last_span = self.last_span; + self.obsolete(last_span, ObsoleteOwnedSelf); + SelfStatic } else { - SelfValue(self_ident) + SelfStatic } } - _ if Parser::token_is_mutability(&self.token) && - self.look_ahead(1, |t| *t == token::TILDE) && - self.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) => { - mutbl_self = self.parse_mutability(); - self.bump(); - drop(self.expect_self_ident()); - let last_span = self.last_span; - self.obsolete(last_span, ObsoleteOwnedSelf); - SelfStatic - } - _ => SelfStatic + _ => SelfStatic, }; let explicit_self_sp = mk_sp(lo, self.span.hi); diff --git a/src/test/compile-fail/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck-lend-flow-match.rs index c6020df2bc2ea..049bec3d37bef 100644 --- a/src/test/compile-fail/borrowck-lend-flow-match.rs +++ b/src/test/compile-fail/borrowck-lend-flow-match.rs @@ -11,9 +11,6 @@ #![allow(unused_variable)] #![allow(dead_assignment)] -fn cond() -> bool { fail!() } -fn link<'a>(v: &'a uint, w: &mut &'a uint) -> bool { *w = v; true } - fn separate_arms() { // Here both arms perform assignments, but only is illegal. @@ -31,28 +28,4 @@ fn separate_arms() { x.clone(); // just to prevent liveness warnings } -fn guard() { - // Here the guard performs a borrow. This borrow "infects" all - // subsequent arms (but not the prior ones). - - let mut a = box 3u; - let mut b = box 4u; - let mut w = &*a; - match 22i { - _ if cond() => { - b = box 5u; - } - - _ if link(&*b, &mut w) => { - b = box 6u; //~ ERROR cannot assign - } - - _ => { - b = box 7u; //~ ERROR cannot assign - } - } - - b = box 8; //~ ERROR cannot assign -} - fn main() {} diff --git a/src/test/compile-fail/borrowck-mutate-in-guard.rs b/src/test/compile-fail/borrowck-mutate-in-guard.rs new file mode 100644 index 0000000000000..8a904a3b5fb89 --- /dev/null +++ b/src/test/compile-fail/borrowck-mutate-in-guard.rs @@ -0,0 +1,33 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Enum<'a> { + A(&'a int), + B(bool), +} + +fn foo() -> int { + let mut n = 42; + let mut x = A(&mut n); + match x { + A(_) if { x = B(false); false } => 1, + //~^ ERROR cannot assign in a pattern guard + A(_) if { let y = &mut x; *y = B(false); false } => 1, + //~^ ERROR cannot mutably borrow in a pattern guard + //~^^ ERROR cannot assign in a pattern guard + A(p) => *p, + B(_) => 2, + } +} + +fn main() { + foo(); +} + diff --git a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot index 251798fc7ed88..f0907d8d708f2 100644 --- a/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f07.dot-expected.dot @@ -7,14 +7,15 @@ digraph block { N5[label="expr 7777i"]; N6[label="expr [7i, 77i, 777i, 7777i]"]; N7[label="expr match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }"]; - N8[label="local x"]; - N9[label="local y"]; - N10[label="pat .."]; - N11[label="pat [x, y, ..]"]; - N12[label="expr x"]; - N13[label="expr y"]; - N14[label="expr x + y"]; - N15[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }; }"]; + N8[label="(dummy_node)"]; + N9[label="local x"]; + N10[label="local y"]; + N11[label="pat .."]; + N12[label="pat [x, y, ..]"]; + N13[label="expr x"]; + N14[label="expr y"]; + N15[label="expr x + y"]; + N16[label="block { match [7i, 77i, 777i, 7777i] { [x, y, ..] => x + y }; }"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -27,7 +28,8 @@ digraph block { N11 -> N12; N12 -> N13; N13 -> N14; - N14 -> N7; - N7 -> N15; - N15 -> N1; + N14 -> N15; + N15 -> N7; + N7 -> N16; + N16 -> N1; } diff --git a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot index 2be43dcaa7b66..dbfa4dd6fe92c 100644 --- a/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot +++ b/src/test/run-make/graphviz-flowgraph/f13.dot-expected.dot @@ -8,18 +8,20 @@ digraph block { N6[label="local _y"]; N7[label="expr x"]; N8[label="expr match x { E13a => _y = 1, E13b(v) => _y = v + 1 }"]; - N9[label="local E13a"]; - N10[label="expr 1"]; - N11[label="expr _y"]; - N12[label="expr _y = 1"]; - N13[label="local v"]; - N14[label="pat E13b(v)"]; - N15[label="expr v"]; - N16[label="expr 1"]; - N17[label="expr v + 1"]; - N18[label="expr _y"]; - N19[label="expr _y = v + 1"]; - N20[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1 }\l}\l"]; + N9[label="(dummy_node)"]; + N10[label="local E13a"]; + N11[label="expr 1"]; + N12[label="expr _y"]; + N13[label="expr _y = 1"]; + N14[label="(dummy_node)"]; + N15[label="local v"]; + N16[label="pat E13b(v)"]; + N17[label="expr v"]; + N18[label="expr 1"]; + N19[label="expr v + 1"]; + N20[label="expr _y"]; + N21[label="expr _y = v + 1"]; + N22[label="block {\l let x = E13b(13);\l let _y;\l match x { E13a => _y = 1, E13b(v) => _y = v + 1 }\l}\l"]; N0 -> N2; N2 -> N3; N3 -> N4; @@ -30,15 +32,17 @@ digraph block { N9 -> N10; N10 -> N11; N11 -> N12; - N12 -> N8; - N7 -> N13; - N13 -> N14; + N12 -> N13; + N13 -> N8; + N9 -> N14; N14 -> N15; N15 -> N16; N16 -> N17; N17 -> N18; N18 -> N19; - N19 -> N8; - N8 -> N20; - N20 -> N1; + N19 -> N20; + N20 -> N21; + N21 -> N8; + N8 -> N22; + N22 -> N1; }