Skip to content

Commit

Permalink
Rollup merge of rust-lang#63230 - tmandry:disallow-possibly-uninitial…
Browse files Browse the repository at this point in the history
…ized, r=Centril

Make use of possibly uninitialized data [E0381] a hard error

This is one of the behaviors we no longer allow in NLL. Since it can
lead to undefined behavior, I think it's definitely worth making it a
hard error without waiting to turn off migration mode (rust-lang#58781).

Closes rust-lang#60450.

My ulterior motive here is making it impossible to leave variables
partially initialized across a yield (see rust-lang#60889, discussion at rust-lang#63035), so
tests are included for that.

cc rust-lang#54987

---

I'm not sure if bypassing the buffer is a good way of doing this. We could also make a `force_errors_buffer` or similar that gets recombined with all the errors as they are emitted. But this is simpler and seems fine to me.

r? @Centril
cc @cramertj @nikomatsakis @pnkfelix @RalfJung
  • Loading branch information
Centril authored Aug 6, 2019
2 parents e988230 + 9058bf2 commit ab7155d
Show file tree
Hide file tree
Showing 20 changed files with 209 additions and 131 deletions.
3 changes: 3 additions & 0 deletions src/librustc_mir/borrow_check/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
);

// This error should not be downgraded to a warning,
// even in migrate mode.
self.disable_error_downgrading();
err.buffer(&mut self.errors_buffer);
} else {
if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
Expand Down
12 changes: 11 additions & 1 deletion src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ fn do_mir_borrowck<'a, 'tcx>(
move_error_reported: BTreeMap::new(),
uninitialized_error_reported: Default::default(),
errors_buffer,
disable_error_downgrading: false,
nonlexical_regioncx: regioncx,
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
Expand Down Expand Up @@ -364,7 +365,7 @@ fn do_mir_borrowck<'a, 'tcx>(
if !mbcx.errors_buffer.is_empty() {
mbcx.errors_buffer.sort_by_key(|diag| diag.span.primary_span());

if tcx.migrate_borrowck() {
if !mbcx.disable_error_downgrading && tcx.migrate_borrowck() {
// When borrowck=migrate, check if AST-borrowck would
// error on the given code.

Expand Down Expand Up @@ -481,6 +482,9 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
uninitialized_error_reported: FxHashSet<PlaceRef<'cx, 'tcx>>,
/// Errors to be reported buffer
errors_buffer: Vec<Diagnostic>,
/// If there are no errors reported by the HIR borrow checker, we downgrade
/// all NLL errors to warnings. Setting this flag disables downgrading.
disable_error_downgrading: bool,
/// This field keeps track of all the local variables that are declared mut and are mutated.
/// Used for the warning issued by an unused mutable local variable.
used_mut: FxHashSet<Local>,
Expand Down Expand Up @@ -921,6 +925,12 @@ impl InitializationRequiringAction {
}

impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// If there are no errors reported by the HIR borrow checker, we downgrade
/// all NLL errors to warnings. Calling this disables downgrading.
crate fn disable_error_downgrading(&mut self) {
self.disable_error_downgrading = true;
}

/// Checks an access to the given place to see if it is allowed. Examines the set of borrows
/// that are in scope, as well as which paths have been initialized, to ensure that (a) the
/// place is initialized and (b) it is not borrowed in some way that would prevent this
Expand Down
44 changes: 44 additions & 0 deletions src/test/ui/async-await/partial-initialization-across-await.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Test that we don't allow awaiting from an async fn while a local is partially
// initialized.

// edition:2018

#![feature(async_await)]

struct S { x: i32, y: i32 }
struct T(i32, i32);

async fn noop() {}

async fn test_tuple() {
let mut t: (i32, i32);
t.0 = 42;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
noop().await;
t.1 = 88;
let _ = t;
}

async fn test_tuple_struct() {
let mut t: T;
t.0 = 42;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
noop().await;
t.1 = 88;
let _ = t;
}

async fn test_struct() {
let mut t: S;
t.x = 42;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
noop().await;
t.y = 88;
let _ = t;
}

fn main() {
let _ = test_tuple();
let _ = test_tuple_struct();
let _ = test_struct();
}
21 changes: 21 additions & 0 deletions src/test/ui/async-await/partial-initialization-across-await.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/partial-initialization-across-await.rs:15:5
|
LL | t.0 = 42;
| ^^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/partial-initialization-across-await.rs:24:5
|
LL | t.0 = 42;
| ^^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/partial-initialization-across-await.rs:33:5
|
LL | t.x = 42;
| ^^^^^^^^ use of possibly uninitialized `t`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0381`.
22 changes: 22 additions & 0 deletions src/test/ui/borrowck/disallow-possibly-uninitialized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Test that we don't allow partial initialization.
// This may be relaxed in the future (see #54987).

fn main() {
let mut t: (u64, u64);
t.0 = 1;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
t.1 = 1;

let mut t: (u64, u64);
t.1 = 1;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
t.0 = 1;

let mut t: (u64, u64);
t.0 = 1;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]

let mut t: (u64,);
t.0 = 1;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
}
27 changes: 27 additions & 0 deletions src/test/ui/borrowck/disallow-possibly-uninitialized.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/disallow-possibly-uninitialized.rs:6:5
|
LL | t.0 = 1;
| ^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/disallow-possibly-uninitialized.rs:11:5
|
LL | t.1 = 1;
| ^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/disallow-possibly-uninitialized.rs:16:5
|
LL | t.0 = 1;
| ^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/disallow-possibly-uninitialized.rs:20:5
|
LL | t.0 = 1;
| ^^^^^^^ use of possibly uninitialized `t`

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0381`.
31 changes: 0 additions & 31 deletions src/test/ui/consts/const_let_refutable.nll.stderr

This file was deleted.

8 changes: 2 additions & 6 deletions src/test/ui/consts/const_let_refutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ fn main() {}

const fn slice([a, b]: &[i32]) -> i32 { //~ ERROR refutable pattern in function argument
a + b //~ ERROR can only call other `const fn` within a `const fn`
//~^ WARN use of possibly uninitialized variable: `a`
//~| WARN this error has been downgraded to a warning for backwards compatibility
//~| WARN this represents potential undefined behavior in your code and this warning will
//~| WARN use of possibly uninitialized variable: `b`
//~| WARN this error has been downgraded to a warning for backwards compatibility
//~| WARN this represents potential undefined behavior in your code and this warning will
//~^ ERROR use of possibly uninitialized variable: `a`
//~| ERROR use of possibly uninitialized variable: `b`
}
14 changes: 3 additions & 11 deletions src/test/ui/consts/const_let_refutable.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,19 @@ LL | a + b
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
= help: add `#![feature(const_fn)]` to the crate attributes to enable

warning[E0381]: use of possibly uninitialized variable: `a`
error[E0381]: use of possibly uninitialized variable: `a`
--> $DIR/const_let_refutable.rs:4:5
|
LL | a + b
| ^ use of possibly uninitialized `a`
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
= note: for more information, try `rustc --explain E0729`

warning[E0381]: use of possibly uninitialized variable: `b`
error[E0381]: use of possibly uninitialized variable: `b`
--> $DIR/const_let_refutable.rs:4:9
|
LL | a + b
| ^ use of possibly uninitialized `b`
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
= note: for more information, try `rustc --explain E0729`

error: aborting due to 2 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0005, E0381, E0723.
For more information about an error, try `rustc --explain E0005`.
23 changes: 0 additions & 23 deletions src/test/ui/empty/empty-never-array.nll.stderr

This file was deleted.

4 changes: 1 addition & 3 deletions src/test/ui/empty/empty-never-array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ fn transmute<T, U>(t: T) -> U {
let Helper::U(u) = Helper::T(t, []);
//~^ ERROR refutable pattern in local binding: `T(_, _)` not covered
u
//~^ WARN use of possibly uninitialized variable: `u`
//~| WARN this error has been downgraded to a warning for backwards compatibility
//~| WARN this represents potential undefined behavior in your code and this warning will
//~^ ERROR use of possibly uninitialized variable: `u`
}

fn main() {
Expand Down
8 changes: 2 additions & 6 deletions src/test/ui/empty/empty-never-array.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ LL | | }
LL | let Helper::U(u) = Helper::T(t, []);
| ^^^^^^^^^^^^ pattern `T(_, _)` not covered

warning[E0381]: use of possibly uninitialized variable: `u`
error[E0381]: use of possibly uninitialized variable: `u`
--> $DIR/empty-never-array.rs:12:5
|
LL | u
| ^ use of possibly uninitialized `u`
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
= note: for more information, try `rustc --explain E0729`

error: aborting due to previous error
error: aborting due to 2 previous errors

Some errors have detailed explanations: E0005, E0381.
For more information about an error, try `rustc --explain E0005`.
46 changes: 46 additions & 0 deletions src/test/ui/generator/partial-initialization-across-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Test that we don't allow yielding from a generator while a local is partially
// initialized.

#![feature(generators)]

struct S { x: i32, y: i32 }
struct T(i32, i32);

fn test_tuple() {
let _ = || {
let mut t: (i32, i32);
t.0 = 42;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
yield;
t.1 = 88;
let _ = t;
};
}

fn test_tuple_struct() {
let _ = || {
let mut t: T;
t.0 = 42;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
yield;
t.1 = 88;
let _ = t;
};
}

fn test_struct() {
let _ = || {
let mut t: S;
t.x = 42;
//~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
yield;
t.y = 88;
let _ = t;
};
}

fn main() {
test_tuple();
test_tuple_struct();
test_struct();
}
21 changes: 21 additions & 0 deletions src/test/ui/generator/partial-initialization-across-yield.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/partial-initialization-across-yield.rs:12:9
|
LL | t.0 = 42;
| ^^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/partial-initialization-across-yield.rs:23:9
|
LL | t.0 = 42;
| ^^^^^^^^ use of possibly uninitialized `t`

error[E0381]: assign to part of possibly uninitialized variable: `t`
--> $DIR/partial-initialization-across-yield.rs:34:9
|
LL | t.x = 42;
| ^^^^^^^^ use of possibly uninitialized `t`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0381`.
16 changes: 0 additions & 16 deletions src/test/ui/issues/issue-15381.nll.stderr

This file was deleted.

4 changes: 1 addition & 3 deletions src/test/ui/issues/issue-15381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ fn main() {
for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
//~^ ERROR refutable pattern in `for` loop binding: `&[]` not covered
println!("y={}", y);
//~^ WARN borrow of possibly uninitialized variable: `y`
//~| WARN this error has been downgraded to a warning for backwards compatibility
//~| WARN this represents potential undefined behavior in your code and this warning will
//~^ ERROR borrow of possibly uninitialized variable: `y`
}
}
Loading

0 comments on commit ab7155d

Please sign in to comment.