Skip to content
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

doc: discuss try! in std::io #12817

Closed
wants to merge 3 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
17 changes: 16 additions & 1 deletion src/librustc/middle/borrowck/gather_loans/gather_moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,22 @@ pub fn gather_assignment(bccx: &BorrowckCtxt,
assignee_loan_path,
assignment_id,
assignment_span,
assignee_id);
assignee_id,
false);
}

pub fn gather_move_and_assignment(bccx: &BorrowckCtxt,
move_data: &MoveData,
assignment_id: ast::NodeId,
assignment_span: Span,
assignee_loan_path: @LoanPath,
assignee_id: ast::NodeId) {
move_data.add_assignment(bccx.tcx,
assignee_loan_path,
assignment_id,
assignment_span,
assignee_id,
true);
}

fn check_is_legal_to_move_from(bccx: &BorrowckCtxt,
Expand Down
54 changes: 29 additions & 25 deletions src/librustc/middle/borrowck/gather_loans/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,20 +214,19 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
visit::walk_expr(this, ex, ());
}

ast::ExprAssign(l, _) | ast::ExprAssignOp(_, l, _) => {
let l_cmt = this.bccx.cat_expr(l);
match opt_loan_path(l_cmt) {
Some(l_lp) => {
gather_moves::gather_assignment(this.bccx, &this.move_data,
ex.id, ex.span,
l_lp, l.id);
}
None => {
// This can occur with e.g. `*foo() = 5`. In such
// cases, there is no need to check for conflicts
// with moves etc, just ignore.
}
}
ast::ExprAssign(l, _) => {
with_assignee_loan_path(
this.bccx, l,
|lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
ex.id, ex.span, lp, l.id));
visit::walk_expr(this, ex, ());
}

ast::ExprAssignOp(_, l, _) => {
with_assignee_loan_path(
this.bccx, l,
|lp| gather_moves::gather_move_and_assignment(this.bccx, &this.move_data,
ex.id, ex.span, lp, l.id));
visit::walk_expr(this, ex, ());
}

Expand Down Expand Up @@ -288,17 +287,10 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,

ast::ExprInlineAsm(ref ia) => {
for &(_, out) in ia.outputs.iter() {
let out_cmt = this.bccx.cat_expr(out);
match opt_loan_path(out_cmt) {
Some(out_lp) => {
gather_moves::gather_assignment(this.bccx, &this.move_data,
ex.id, ex.span,
out_lp, out.id);
}
None => {
// See the comment for ExprAssign.
}
}
with_assignee_loan_path(
this.bccx, out,
|lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
ex.id, ex.span, lp, out.id));
}
visit::walk_expr(this, ex, ());
}
Expand All @@ -309,6 +301,18 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
}
}

fn with_assignee_loan_path(bccx: &BorrowckCtxt, expr: &ast::Expr, op: |@LoanPath|) {
let cmt = bccx.cat_expr(expr);
match opt_loan_path(cmt) {
Some(lp) => op(lp),
None => {
// This can occur with e.g. `*foo() = 5`. In such
// cases, there is no need to check for conflicts
// with moves etc, just ignore.
}
}
}

impl<'a> GatherLoanCtxt<'a> {
pub fn tcx(&self) -> ty::ctxt { self.bccx.tcx }

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ impl BorrowckCtxt {
move_data::Declared => {
self.tcx.sess.span_err(
use_span,
format!("{} of possibly uninitialized value: `{}`",
format!("{} of possibly uninitialized variable: `{}`",
verb,
self.loan_path_to_str(lp)));
}
Expand Down
15 changes: 9 additions & 6 deletions src/librustc/middle/borrowck/move_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,25 @@ use util::ppaux::Repr;

pub struct MoveData {
/// Move paths. See section "Move paths" in `doc.rs`.
paths: RefCell<Vec<MovePath> >,
paths: RefCell<Vec<MovePath>>,

/// Cache of loan path to move path index, for easy lookup.
path_map: RefCell<HashMap<@LoanPath, MovePathIndex>>,

/// Each move or uninitialized variable gets an entry here.
moves: RefCell<Vec<Move> >,
moves: RefCell<Vec<Move>>,

/// Assignments to a variable, like `x = foo`. These are assigned
/// bits for dataflow, since we must track them to ensure that
/// immutable variables are assigned at most once along each path.
var_assignments: RefCell<Vec<Assignment> >,
var_assignments: RefCell<Vec<Assignment>>,

/// Assignments to a path, like `x.f = foo`. These are not
/// assigned dataflow bits, but we track them because they still
/// kill move bits.
path_assignments: RefCell<Vec<Assignment> >,
path_assignments: RefCell<Vec<Assignment>>,

/// Assignments to a variable or path, like `x = foo`, but not `x += foo`.
assignee_ids: RefCell<HashSet<ast::NodeId>>,
}

Expand Down Expand Up @@ -392,7 +394,8 @@ impl MoveData {
lp: @LoanPath,
assign_id: ast::NodeId,
span: Span,
assignee_id: ast::NodeId) {
assignee_id: ast::NodeId,
is_also_move: bool) {
/*!
* Adds a new record for an assignment to `lp` that occurs at
* location `id` with the given `span`.
Expand All @@ -403,7 +406,7 @@ impl MoveData {

let path_index = self.move_path(tcx, lp);

{
if !is_also_move {
let mut assignee_ids = self.assignee_ids.borrow_mut();
assignee_ids.get().insert(assignee_id);
}
Expand Down
62 changes: 8 additions & 54 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1468,28 +1468,14 @@ impl Liveness {

fn check_local(this: &mut Liveness, local: &Local) {
match local.init {
Some(_) => {
this.warn_about_unused_or_dead_vars_in_pat(local.pat);
}
None => {

// No initializer: the variable might be unused; if not, it
// should not be live at this point.

debug!("check_local() with no initializer");
this.pat_bindings(local.pat, |ln, var, sp, id| {
if !this.warn_about_unused(sp, id, ln, var) {
match this.live_on_exit(ln, var) {
None => { /* not live: good */ }
Some(lnk) => {
this.report_illegal_read(
local.span, lnk, var,
PossiblyUninitializedVariable);
}
}
}
})
}
Some(_) => {
this.warn_about_unused_or_dead_vars_in_pat(local.pat);
},
None => {
this.pat_bindings(local.pat, |ln, var, sp, id| {
this.warn_about_unused(sp, id, ln, var);
})
}
}

visit::walk_local(this, local, ());
Expand Down Expand Up @@ -1644,38 +1630,6 @@ impl Liveness {
}
}

pub fn report_illegal_read(&self,
chk_span: Span,
lnk: LiveNodeKind,
var: Variable,
rk: ReadKind) {
let msg = match rk {
PossiblyUninitializedVariable => "possibly uninitialized \
variable",
PossiblyUninitializedField => "possibly uninitialized field",
MovedValue => "moved value",
PartiallyMovedValue => "partially moved value"
};
let name = self.ir.variable_name(var);
match lnk {
FreeVarNode(span) => {
self.tcx.sess.span_err(
span,
format!("capture of {}: `{}`", msg, name));
}
ExprNode(span) => {
self.tcx.sess.span_err(
span,
format!("use of {}: `{}`", msg, name));
}
ExitNode | VarDefNode(_) => {
self.tcx.sess.span_bug(
chk_span,
format!("illegal reader: {:?}", lnk));
}
}
}

pub fn should_warn(&self, var: Variable) -> Option<~str> {
let name = self.ir.variable_name(var);
if name.len() == 0 || name[0] == ('_' as u8) { None } else { Some(name) }
Expand Down
34 changes: 34 additions & 0 deletions src/libstd/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,40 @@ need to inspect or unwrap the `IoResult<File>` and we simply call `write_line`
on it. If `new` returned an `Err(..)` then the followup call to `write_line`
will also return an error.

## `try!`

Explicit pattern matching on `IoResult`s can get quite verbose, especially
when performing many I/O operations. Some examples (like those above) are
alleviated with extra methods implemented on `IoResult`, but others have more
complex interdependencies among each I/O operation.

The `try!` macro from `std::macros` is provided as a method of early-return
inside `Result`-returning functions. It expands to an early-return on `Err`
and otherwise unwraps the contained `Ok` value.

If you wanted to read several `u32`s from a file and return their product:

```rust
use std::io::{File, IoResult};

fn file_product(p: &Path) -> IoResult<u32> {
let mut f = File::open(p);
let x1 = try!(f.read_le_u32());
let x2 = try!(f.read_le_u32());

Ok(x1 * x2)
}

match file_product(&Path::new("numbers.bin")) {
Ok(x) => println!("{}", x),
Err(e) => println!("Failed to read numbers!")
}
```

With `try!` in `file_product`, each `read_le_u32` need not be directly
concerned with error handling; instead its caller is responsible for
responding to errors that may occur while attempting to read the numbers.

*/

#[deny(unused_must_use)];
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/asm-out-read-uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn foo(x: int) { info!("{}", x); }
pub fn main() {
let x: int;
unsafe {
asm!("mov $1, $0" : "=r"(x) : "r"(x)); //~ ERROR use of possibly uninitialized value: `x`
asm!("mov $1, $0" : "=r"(x) : "r"(x)); //~ ERROR use of possibly uninitialized variable: `x`
}
foo(x);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
fn force(f: ||) { f(); }
fn main() {
let x: int;
force(|| {
info!("{}", x); //~ ERROR capture of possibly uninitialized variable: `x`
force(|| { //~ ERROR capture of possibly uninitialized variable: `x`
info!("{}", x);
});
}
44 changes: 44 additions & 0 deletions src/test/compile-fail/borrowck-uninit-in-assignop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2012-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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Tests that the use of uninitialized variable in assignment operator
// expression is detected.

pub fn main() {
let x: int;
x += 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x -= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x *= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x /= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x %= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x ^= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x &= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x |= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x <<= 1; //~ ERROR use of possibly uninitialized variable: `x`

let x: int;
x >>= 1; //~ ERROR use of possibly uninitialized variable: `x`
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

fn test() {
let w: ~[int];
w[5] = 0; //~ ERROR use of possibly uninitialized variable: `w`
//~^ ERROR cannot assign to immutable vec content `w[..]`

let mut w: ~[int];
w[5] = 0; //~ ERROR use of possibly uninitialized variable: `w`
}

Expand Down
5 changes: 5 additions & 0 deletions src/test/compile-fail/liveness-unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ fn f1b(x: &mut int) {
#[allow(unused_variable)]
fn f1c(x: int) {}

fn f1d() {
let x: int;
//~^ ERROR unused variable: `x`
}

fn f2() {
let x = 3;
//~^ ERROR unused variable: `x`
Expand Down