Skip to content

Commit a4e0945

Browse files
committed
Unions reinitialized after assignment into field.
This commit makes two changes: First, it updates the dataflow builder to add an init for the place containing a union if there is an assignment into the field of that union. Second, it stops a "use of uninitialized" error occuring when there is an assignment into the field of an uninitialized union that was previously initialized. Making this assignment would re-initialize the union, as tested in `src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr`. The check for previous initialization ensures that we do not start supporting partial initialization yet (cc #21232, #54499, #54986).
1 parent 87a3c1e commit a4e0945

File tree

4 files changed

+50
-31
lines changed

4 files changed

+50
-31
lines changed

Diff for: src/librustc_mir/borrow_check/mod.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -1719,12 +1719,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17191719
}
17201720
}
17211721

1722-
fn check_parent_of_field<'cx, 'gcx, 'tcx>(this: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
1723-
context: Context,
1724-
base: &Place<'tcx>,
1725-
span: Span,
1726-
flow_state: &Flows<'cx, 'gcx, 'tcx>)
1727-
{
1722+
fn check_parent_of_field<'cx, 'gcx, 'tcx>(
1723+
this: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
1724+
context: Context,
1725+
base: &Place<'tcx>,
1726+
span: Span,
1727+
flow_state: &Flows<'cx, 'gcx, 'tcx>,
1728+
) {
17281729
// rust-lang/rust#21232: Until Rust allows reads from the
17291730
// initialized parts of partially initialized structs, we
17301731
// will, starting with the 2018 edition, reject attempts
@@ -1776,6 +1777,22 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17761777
}
17771778

17781779
if let Some((prefix, mpi)) = shortest_uninit_seen {
1780+
// Check for a reassignment into a uninitialized field of a union (for example,
1781+
// after a move out). In this case, do not report a error here. There is an
1782+
// exception, if this is the first assignment into the union (that is, there is
1783+
// no move out from an earlier location) then this is an attempt at initialization
1784+
// of the union - we should error in that case.
1785+
let tcx = this.infcx.tcx;
1786+
if let ty::TyKind::Adt(def, _) = base.ty(this.mir, tcx).to_ty(tcx).sty {
1787+
let moved_before_this = this.move_data.path_map[mpi].iter().any(|moi| {
1788+
this.move_data.moves[*moi].source < context.loc
1789+
});
1790+
1791+
if def.is_union() && moved_before_this {
1792+
return;
1793+
}
1794+
}
1795+
17791796
this.report_use_of_moved_or_uninitialized(
17801797
context,
17811798
InitializationRequiringAction::PartialAssignment,

Diff for: src/librustc_mir/dataflow/move_paths/builder.rs

+14
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,20 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
430430
fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) {
431431
debug!("gather_init({:?}, {:?})", self.loc, place);
432432

433+
let place = match place {
434+
// Check if we are assigning into a field of a union, if so, lookup the place
435+
// of the union so it is marked as initialized again.
436+
Place::Projection(box Projection {
437+
base,
438+
elem: ProjectionElem::Field(_, _),
439+
}) if match base.ty(self.builder.mir, self.builder.tcx).to_ty(self.builder.tcx).sty {
440+
ty::TyKind::Adt(def, _) if def.is_union() => true,
441+
_ => false,
442+
} => base,
443+
// Otherwise, lookup the place.
444+
_ => place,
445+
};
446+
433447
if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) {
434448
let init = self.builder.data.inits.push(Init {
435449
location: InitLocation::Statement(self.loc),

Diff for: src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr

+1-23
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,6 @@ LL | let a = u.a; //~ ERROR use of moved value: `u.a`
88
|
99
= note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
1010

11-
error[E0382]: use of moved value: `u`
12-
--> $DIR/borrowck-union-move-assign.rs:33:21
13-
|
14-
LL | let a = u.a;
15-
| --- value moved here
16-
LL | u.a = A;
17-
LL | let a = u.a; // OK
18-
| ^^^ value used here after move
19-
|
20-
= note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
21-
22-
error[E0382]: use of moved value: `u`
23-
--> $DIR/borrowck-union-move-assign.rs:39:21
24-
|
25-
LL | let a = u.a;
26-
| --- value moved here
27-
LL | u.b = B;
28-
LL | let a = u.a; // OK
29-
| ^^^ value used here after move
30-
|
31-
= note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
32-
33-
error: aborting due to 3 previous errors
11+
error: aborting due to previous error
3412

3513
For more information about this error, try `rustc --explain E0382`.

Diff for: src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr

+12-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ error[E0381]: assign to part of possibly uninitialized variable: `x`
44
LL | x.a = 1; //~ ERROR
55
| ^^^^^^^ use of possibly uninitialized `x`
66

7-
error: aborting due to previous error
7+
error[E0594]: cannot assign to `x.b`, as `x` is not declared as mutable
8+
--> $DIR/reassignment_immutable_fields_overlapping.rs:23:5
9+
|
10+
LL | let x: Foo;
11+
| - help: consider changing this to be mutable: `mut x`
12+
LL | x.a = 1; //~ ERROR
13+
LL | x.b = 22; //~ ERROR
14+
| ^^^^^^^^ cannot assign
15+
16+
error: aborting due to 2 previous errors
817

9-
For more information about this error, try `rustc --explain E0381`.
18+
Some errors occurred: E0381, E0594.
19+
For more information about an error, try `rustc --explain E0381`.

0 commit comments

Comments
 (0)