Skip to content

Commit 08399b0

Browse files
committed
librustc: Forbid partial reinitialization of uninitialized structures or
enumerations that implement the `Drop` trait. This breaks code like: struct Struct { f: String, g: String, } impl Drop for Struct { ... } fn main() { let x = Struct { ... }; drop(x); x.f = ...; } Change this code to not create partially-initialized structures. For example: struct Struct { f: String, g: String, } impl Drop for Struct { ... } fn main() { let x = Struct { ... }; drop(x); x = Struct { f: ..., g: ..., } } Closes rust-lang#18571. [breaking-change]
1 parent 1bf0649 commit 08399b0

File tree

4 files changed

+131
-2
lines changed

4 files changed

+131
-2
lines changed

src/librustc/middle/borrowck/check_loans.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,13 +748,26 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
748748
if mode != euv::Init {
749749
check_for_assignment_to_borrowed_path(
750750
self, assignment_id, assignment_span, assignee_cmt.clone());
751-
mark_variable_as_used_mut(self, assignee_cmt);
751+
mark_variable_as_used_mut(self, assignee_cmt.clone());
752752
}
753753
}
754+
755+
// Check for partial reinitialization of a fully uninitialized structure.
756+
match assignee_cmt.cat {
757+
mc::cat_interior(ref container_cmt, _) | mc::cat_downcast(ref container_cmt) => {
758+
check_for_illegal_initialization(self,
759+
assignment_id,
760+
assignment_span,
761+
container_cmt);
762+
}
763+
mc::cat_rvalue(_) | mc::cat_static_item | mc::cat_upvar(_) | mc::cat_local(_) |
764+
mc::cat_deref(_, _, _) => {}
765+
}
754766
return;
755767
}
756768

757-
// Initializations are OK.
769+
// Initializations are OK if and only if they aren't partial
770+
// reinitialization of a partially-uninitialized structure.
758771
if mode == euv::Init {
759772
return
760773
}
@@ -929,6 +942,39 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
929942
false
930943
});
931944
}
945+
946+
fn check_for_illegal_initialization(this: &CheckLoanCtxt,
947+
assignment_id: ast::NodeId,
948+
span: Span,
949+
assignee_cmt: &mc::cmt) {
950+
let local_id = match assignee_cmt.cat {
951+
mc::cat_interior(ref container_cmt, _) | mc::cat_downcast(ref container_cmt) => {
952+
return check_for_illegal_initialization(this,
953+
assignment_id,
954+
span,
955+
container_cmt)
956+
}
957+
mc::cat_local(local_id) => local_id,
958+
mc::cat_rvalue(_) | mc::cat_static_item | mc::cat_upvar(_) |
959+
mc::cat_deref(..) => return,
960+
};
961+
962+
let struct_id = match ty::get(assignee_cmt.ty).sty {
963+
ty::ty_struct(def_id, _) => def_id,
964+
_ => return,
965+
};
966+
if !ty::has_dtor(this.tcx(), struct_id) {
967+
return
968+
}
969+
970+
let loan_path = Rc::new(LpVar(local_id));
971+
this.move_data.each_move_of(assignment_id, &loan_path, |_, _| {
972+
this.bccx.report_partial_reinitialization_of_uninitialized_structure(
973+
span,
974+
&*loan_path);
975+
false
976+
});
977+
}
932978
}
933979

934980
pub fn report_illegal_mutation(&self,

src/librustc/middle/borrowck/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,18 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
597597
}
598598
}
599599

600+
pub fn report_partial_reinitialization_of_uninitialized_structure(
601+
&self,
602+
span: Span,
603+
lp: &LoanPath) {
604+
self.tcx
605+
.sess
606+
.span_err(span,
607+
(format!("partial reinitialization of uninitialized \
608+
structure `{}`",
609+
self.loan_path_to_string(lp))).as_slice());
610+
}
611+
600612
pub fn report_reassigned_immutable_variable(&self,
601613
span: Span,
602614
lp: &LoanPath,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Test;
12+
13+
struct Test2 {
14+
b: Option<Test>,
15+
}
16+
17+
impl Drop for Test {
18+
fn drop(&mut self) {
19+
println!("dropping!");
20+
}
21+
}
22+
23+
impl Drop for Test2 {
24+
fn drop(&mut self) {}
25+
}
26+
27+
fn stuff() {
28+
let mut t = Test2 { b: None };
29+
let u = Test;
30+
drop(t);
31+
t.b = Some(u);
32+
//~^ ERROR partial reinitialization of uninitialized structure
33+
}
34+
35+
fn main() {
36+
stuff()
37+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Test {
12+
a: int,
13+
b: Option<Box<Test>>,
14+
}
15+
16+
impl Drop for Test {
17+
fn drop(&mut self) {
18+
println!("Dropping {}", self.a);
19+
}
20+
}
21+
22+
fn stuff() {
23+
let mut t = Test { a: 1, b: None};
24+
let mut u = Test { a: 2, b: Some(box t)};
25+
t.b = Some(box u);
26+
//~^ ERROR partial reinitialization of uninitialized structure
27+
println!("done");
28+
}
29+
30+
fn main() {
31+
stuff();
32+
println!("Hello, world!")
33+
}
34+

0 commit comments

Comments
 (0)