From 3f778f31b6b285d41973a36ee1f4a2291d5f6a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 28 Oct 2021 00:00:00 +0000 Subject: [PATCH] Use type based qualification for unions 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 } const A: U = U { i: 0 }; const B: std::cell::Cell = 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. --- .../src/transform/check_consts/qualifs.rs | 3 + .../src/transform/check_consts/resolver.rs | 12 +++- src/test/ui/consts/qualif-union.rs | 32 +++++++++++ src/test/ui/consts/qualif-union.stderr | 57 +++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/consts/qualif-union.rs create mode 100644 src/test/ui/consts/qualif-union.stderr diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index aa42f8936f3..0fdb772c262 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -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... diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index e20b86dd452..f516c07243e 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -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); diff --git a/src/test/ui/consts/qualif-union.rs b/src/test/ui/consts/qualif-union.rs new file mode 100644 index 00000000000..2054b5b89ed --- /dev/null +++ b/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 } + +const C1: Cell = { + unsafe { U { c: Cell::new(0) }.c } +}; + +const C2: Cell = { + unsafe { U { i : 0 }.c } +}; + +const C3: Cell = { + 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 +} diff --git a/src/test/ui/consts/qualif-union.stderr b/src/test/ui/consts/qualif-union.stderr new file mode 100644 index 00000000000..fda8ad4a3bc --- /dev/null +++ b/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`.