Skip to content

Commit

Permalink
Use type based qualification for unions
Browse files Browse the repository at this point in the history
Union field access is currently qualified based on the qualification of
a value previously assigned to the union. At the same time, every union
access transmutes the content of the union, which might result in a
different qualification.

For example, consider constants A and B as defined below, under the
current rules neither contains interior mutability, since a value used
in the initial assignment did not contain `UnsafeCell` constructor.

```rust
#![feature(untagged_unions)]

union U { i: u32, c: std::cell::Cell<u32> }
const A: U = U { i: 0 };
const B: std::cell::Cell<u32> = unsafe { U { i: 0 }.c };
```

To avoid the issue, the changes here propose to consider the content of
a union as opaque and use type based qualification for union types.
  • Loading branch information
tmiasko committed Oct 28, 2021
1 parent 4e0d397 commit 3f778f3
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 1 deletion.
Expand Up @@ -258,6 +258,9 @@ where
if Q::in_adt_inherently(cx, def, substs) {
return true;
}
if def.is_union() && Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) {
return true;
}
}

// Otherwise, proceed structurally...
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
Expand Up @@ -42,9 +42,19 @@ where
}
}

fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) {
fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, mut value: bool) {
debug_assert!(!place.is_indirect());

if !value {
for (base, _elem) in place.iter_projections() {
let base_ty = base.ty(self.ccx.body, self.ccx.tcx);
if base_ty.ty.is_union() && Q::in_any_value_of_ty(self.ccx, base_ty.ty) {
value = true;
break;
}
}
}

match (value, place.as_ref()) {
(true, mir::PlaceRef { local, .. }) => {
self.qualifs_per_local.insert(local);
Expand Down
32 changes: 32 additions & 0 deletions src/test/ui/consts/qualif-union.rs
@@ -0,0 +1,32 @@
// Checks that unions use type based qualification. Regression test for issue #90268.
#![feature(untagged_unions)]
use std::cell::Cell;

union U { i: u32, c: Cell<u32> }

const C1: Cell<u32> = {
unsafe { U { c: Cell::new(0) }.c }
};

const C2: Cell<u32> = {
unsafe { U { i : 0 }.c }
};

const C3: Cell<u32> = {
let mut u = U { i: 0 };
u.i = 1;
unsafe { u.c }
};

const C4: U = U { i: 0 };

const C5: [U; 1] = [U {i : 0}; 1];

fn main() {
// Interior mutability should prevent promotion.
let _: &'static _ = &C1; //~ ERROR temporary value dropped while borrowed
let _: &'static _ = &C2; //~ ERROR temporary value dropped while borrowed
let _: &'static _ = &C3; //~ ERROR temporary value dropped while borrowed
let _: &'static _ = &C4; //~ ERROR temporary value dropped while borrowed
let _: &'static _ = &C5; //~ ERROR temporary value dropped while borrowed
}
57 changes: 57 additions & 0 deletions src/test/ui/consts/qualif-union.stderr
@@ -0,0 +1,57 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/qualif-union.rs:27:26
|
LL | let _: &'static _ = &C1;
| ---------- ^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement

error[E0716]: temporary value dropped while borrowed
--> $DIR/qualif-union.rs:28:26
|
LL | let _: &'static _ = &C2;
| ---------- ^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement

error[E0716]: temporary value dropped while borrowed
--> $DIR/qualif-union.rs:29:26
|
LL | let _: &'static _ = &C3;
| ---------- ^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
...
LL | }
| - temporary value is freed at the end of this statement

error[E0716]: temporary value dropped while borrowed
--> $DIR/qualif-union.rs:30:26
|
LL | let _: &'static _ = &C4;
| ---------- ^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
LL | let _: &'static _ = &C5;
LL | }
| - temporary value is freed at the end of this statement

error[E0716]: temporary value dropped while borrowed
--> $DIR/qualif-union.rs:31:26
|
LL | let _: &'static _ = &C5;
| ---------- ^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
LL | }
| - temporary value is freed at the end of this statement

error: aborting due to 5 previous errors

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

0 comments on commit 3f778f3

Please sign in to comment.