From ac1ed220e1d970854678ec7002ec0c4b671a9c5f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 1 Mar 2024 12:22:05 +0100 Subject: [PATCH] add an interesting test; make const-check understand that empty arrays are not interior mutable --- .../src/transform/check_consts/check.rs | 17 ++++++++++++++++- tests/ui/consts/enclosing-scope-rule.rs | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/ui/consts/enclosing-scope-rule.rs diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 165d91aface44..22331e5739b66 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -373,6 +373,21 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { } } } + + fn has_interior_mut(&self, ty: Ty<'tcx>) -> bool { + match ty.kind() { + // Empty arrays have no interior mutability no matter their element type. + ty::Array(_elem, count) + if count + .try_eval_target_usize(self.tcx, self.param_env) + .is_some_and(|v| v == 0) => + { + false + } + // Fallback to checking `Freeze`. + _ => !ty.is_freeze(self.tcx, self.param_env), + } + } } impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { @@ -487,7 +502,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // We don't do value-based reasoning here, since the rules for interior mutability // are not finalized yet and they seem likely to not be full value-based in the end. let borrowed_place_has_mut_interior = - !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env); + self.has_interior_mut(place.ty(self.body, self.tcx).ty); // If the place is indirect, this is basically a reborrow. We have a reborrow // special case above, but for raw pointers and pointers/references to `static` and diff --git a/tests/ui/consts/enclosing-scope-rule.rs b/tests/ui/consts/enclosing-scope-rule.rs new file mode 100644 index 0000000000000..7041ad9af34e0 --- /dev/null +++ b/tests/ui/consts/enclosing-scope-rule.rs @@ -0,0 +1,16 @@ +//@build-pass +// Some code that looks like it might be relying on promotion, but actually this is using the +// enclosing-scope rule, meaning the reference is "extended" to outlive its block and live as long +// as the surrounding block (which in this case is the entire program). There are multiple +// allocations being interned at once. + +struct Gen(T); +impl<'a, T> Gen<&'a T> { + // Can't be promoted because `T` might not be `'static`. + const C: &'a [T] = &[]; +} + +// Can't be promoted because of `Drop`. +const V: &Vec = &Vec::new(); + +fn main() {}