Skip to content

Commit d3514a0

Browse files
committed
Ensure nested allocations in statics do not get deduplicated
1 parent 92414ab commit d3514a0

File tree

21 files changed

+259
-54
lines changed

21 files changed

+259
-54
lines changed

compiler/rustc_codegen_gcc/src/mono_item.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#[cfg(feature = "master")]
22
use gccjit::{FnAttribute, VarAttribute};
33
use rustc_codegen_ssa::traits::PreDefineMethods;
4+
use rustc_hir::def::DefKind;
45
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6+
use rustc_middle::bug;
57
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
68
use rustc_middle::mir::mono::{Linkage, Visibility};
79
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
@@ -23,7 +25,14 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
2325
) {
2426
let attrs = self.tcx.codegen_fn_attrs(def_id);
2527
let instance = Instance::mono(self.tcx, def_id);
26-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
28+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
29+
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
30+
// the gcc type from the actual evaluated initializer.
31+
let ty = if nested {
32+
self.tcx.types.unit
33+
} else {
34+
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
35+
};
2736
let gcc_type = self.layout_of(ty).gcc_type(self);
2837

2938
let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);

compiler/rustc_codegen_llvm/src/consts.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::type_::Type;
99
use crate::type_of::LayoutLlvmExt;
1010
use crate::value::Value;
1111
use rustc_codegen_ssa::traits::*;
12+
use rustc_hir::def::DefKind;
1213
use rustc_hir::def_id::DefId;
1314
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
1415
use rustc_middle::mir::interpret::{
@@ -229,9 +230,17 @@ impl<'ll> CodegenCx<'ll, '_> {
229230
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
230231
let instance = Instance::mono(self.tcx, def_id);
231232
trace!(?instance);
232-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
233-
trace!(?ty);
234-
let llty = self.layout_of(ty).llvm_type(self);
233+
234+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
235+
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
236+
// the llvm type from the actual evaluated initializer.
237+
let llty = if nested {
238+
self.type_i8()
239+
} else {
240+
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
241+
trace!(?ty);
242+
self.layout_of(ty).llvm_type(self)
243+
};
235244
self.get_static_inner(def_id, llty)
236245
}
237246

@@ -346,6 +355,12 @@ impl<'ll> CodegenCx<'ll, '_> {
346355

347356
fn codegen_static_item(&self, def_id: DefId) {
348357
unsafe {
358+
assert!(
359+
llvm::LLVMGetInitializer(
360+
self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap()
361+
)
362+
.is_none()
363+
);
349364
let attrs = self.tcx.codegen_fn_attrs(def_id);
350365

351366
let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
2626
use rustc_codegen_ssa::traits::*;
2727
use rustc_fs_util::path_to_c_string;
2828
use rustc_hir::def::CtorKind;
29+
use rustc_hir::def::DefKind;
2930
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
3031
use rustc_middle::bug;
3132
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
@@ -1309,6 +1310,11 @@ pub fn build_global_var_di_node<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, glo
13091310
};
13101311

13111312
let is_local_to_unit = is_node_local_to_unit(cx, def_id);
1313+
1314+
let DefKind::Static { nested, .. } = cx.tcx.def_kind(def_id) else { bug!() };
1315+
if nested {
1316+
return;
1317+
}
13121318
let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all());
13131319
let type_di_node = type_di_node(cx, variable_type);
13141320
let var_name = tcx.item_name(def_id);

compiler/rustc_codegen_llvm/src/mono_item.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::errors::SymbolAlreadyDefined;
55
use crate::llvm;
66
use crate::type_of::LayoutLlvmExt;
77
use rustc_codegen_ssa::traits::*;
8+
use rustc_hir::def::DefKind;
89
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
10+
use rustc_middle::bug;
911
use rustc_middle::mir::mono::{Linkage, Visibility};
1012
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
1113
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
@@ -21,7 +23,14 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
2123
symbol_name: &str,
2224
) {
2325
let instance = Instance::mono(self.tcx, def_id);
24-
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
26+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
27+
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
28+
// the llvm type from the actual evaluated initializer.
29+
let ty = if nested {
30+
self.tcx.types.unit
31+
} else {
32+
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
33+
};
2534
let llty = self.layout_of(ty).llvm_type(self);
2635

2736
let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
5959
};
6060

6161
let ret = if let InternKind::Static(_) = intern_kind {
62-
create_static_alloc(ecx, cid.instance.def_id(), layout)?
62+
create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
6363
} else {
6464
ecx.allocate(layout, MemoryKind::Stack)?
6565
};
@@ -380,7 +380,11 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
380380
}
381381
Ok(mplace) => {
382382
// Since evaluation had no errors, validate the resulting constant.
383+
384+
// Temporarily allow access to the static_root_ids for the purpose of validation.
385+
let static_root_ids = ecx.machine.static_root_ids.take();
383386
let res = const_validate_mplace(&ecx, &mplace, cid);
387+
ecx.machine.static_root_ids = static_root_ids;
384388

385389
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
386390

compiler/rustc_const_eval/src/const_eval/machine.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxIndexMap;
88
use rustc_data_structures::fx::IndexEntry;
99
use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::DefId;
11+
use rustc_hir::def_id::LocalDefId;
1112
use rustc_hir::LangItem;
1213
use rustc_middle::mir;
1314
use rustc_middle::mir::AssertMessage;
@@ -59,8 +60,10 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
5960
/// Whether to check alignment during evaluation.
6061
pub(super) check_alignment: CheckAlignment,
6162

62-
/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization.
63-
pub(crate) static_root_alloc_id: Option<AllocId>,
63+
/// If `Some`, we are evaluating the initializer of the static with the given `LocalDefId`,
64+
/// storing the result in the given `AllocId`.
65+
/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization loops.
66+
pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>,
6467
}
6568

6669
#[derive(Copy, Clone)]
@@ -94,7 +97,7 @@ impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
9497
stack: Vec::new(),
9598
can_access_mut_global,
9699
check_alignment,
97-
static_root_alloc_id: None,
100+
static_root_ids: None,
98101
}
99102
}
100103
}
@@ -749,7 +752,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
749752
ecx: &InterpCx<'mir, 'tcx, Self>,
750753
alloc_id: AllocId,
751754
) -> InterpResult<'tcx> {
752-
if Some(alloc_id) == ecx.machine.static_root_alloc_id {
755+
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
753756
Err(ConstEvalErrKind::RecursiveStatic.into())
754757
} else {
755758
Ok(())

compiler/rustc_const_eval/src/interpret/intern.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@
1313
//! but that would require relying on type information, and given how many ways Rust has to lie
1414
//! about type information, we want to avoid doing that.
1515
16+
use hir::def::DefKind;
1617
use rustc_ast::Mutability;
1718
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
1819
use rustc_errors::ErrorGuaranteed;
1920
use rustc_hir as hir;
20-
use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult};
21+
use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
22+
use rustc_middle::query::TyCtxtAt;
2123
use rustc_middle::ty::layout::TyAndLayout;
24+
use rustc_span::def_id::LocalDefId;
25+
use rustc_span::sym;
2226

2327
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
2428
use crate::const_eval;
@@ -33,7 +37,19 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
3337
FrameExtra = (),
3438
AllocExtra = (),
3539
MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>,
36-
>;
40+
> + HasStaticRootDefId;
41+
42+
pub trait HasStaticRootDefId {
43+
/// Returns the `DefId` of the static item that is currently being evaluated.
44+
/// Used for interning to be able to handle nested allocations.
45+
fn static_def_id(&self) -> Option<LocalDefId>;
46+
}
47+
48+
impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_, '_> {
49+
fn static_def_id(&self) -> Option<LocalDefId> {
50+
Some(self.static_root_ids?.1)
51+
}
52+
}
3753

3854
/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory.
3955
///
@@ -67,10 +83,35 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
6783
}
6884
// link the alloc id to the actual allocation
6985
let alloc = ecx.tcx.mk_const_alloc(alloc);
70-
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
86+
if let Some(static_id) = ecx.machine.static_def_id() {
87+
intern_as_new_static(ecx.tcx, static_id, alloc_id, alloc);
88+
} else {
89+
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
90+
}
7191
Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
7292
}
7393

94+
/// Creates a new `DefId` and feeds all the right queries to make this `DefId`
95+
/// appear as if it were a user-written `static` (though it has no HIR).
96+
fn intern_as_new_static<'tcx>(
97+
tcx: TyCtxtAt<'tcx>,
98+
static_id: LocalDefId,
99+
alloc_id: AllocId,
100+
alloc: ConstAllocation<'tcx>,
101+
) {
102+
let feed = tcx.create_def(
103+
static_id,
104+
sym::nested,
105+
DefKind::Static { mt: alloc.0.mutability, nested: true },
106+
);
107+
tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
108+
feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone());
109+
feed.eval_static_initializer(Ok(alloc));
110+
feed.generics_of(tcx.generics_of(static_id).clone());
111+
feed.def_ident_span(tcx.def_ident_span(static_id));
112+
feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id));
113+
}
114+
74115
/// How a constant value should be interned.
75116
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
76117
pub enum InternKind {

compiler/rustc_const_eval/src/interpret/memory.rs

+28-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::ptr;
1515

1616
use rustc_ast::Mutability;
1717
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
18+
use rustc_hir::def::DefKind;
1819
use rustc_middle::mir::display_allocation;
1920
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
2021
use rustc_target::abi::{Align, HasDataLayout, Size};
@@ -761,19 +762,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
761762
// be held throughout the match.
762763
match self.tcx.try_get_global_alloc(id) {
763764
Some(GlobalAlloc::Static(def_id)) => {
764-
assert!(self.tcx.is_static(def_id));
765765
// Thread-local statics do not have a constant address. They *must* be accessed via
766766
// `ThreadLocalRef`; we can never have a pointer to them as a regular constant value.
767767
assert!(!self.tcx.is_thread_local_static(def_id));
768-
// Use size and align of the type.
769-
let ty = self
770-
.tcx
771-
.type_of(def_id)
772-
.no_bound_vars()
773-
.expect("statics should not have generic parameters");
774-
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
775-
assert!(layout.is_sized());
776-
(layout.size, layout.align.abi, AllocKind::LiveData)
768+
769+
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else {
770+
bug!("GlobalAlloc::Static is not a static")
771+
};
772+
773+
let (size, align) = if nested {
774+
// Nested anonymous statics are untyped, so let's get their
775+
// size and alignment from the allocaiton itself. This always
776+
// succeeds, as the query is fed at DefId creation time, so no
777+
// evaluation actually occurs.
778+
let alloc = self.tcx.eval_static_initializer(def_id).unwrap();
779+
(alloc.0.size(), alloc.0.align)
780+
} else {
781+
// Use size and align of the type for everything else. We need
782+
// to do that to
783+
// * avoid cycle errors in case of self-referential statics,
784+
// * be able to get information on extern statics.
785+
let ty = self
786+
.tcx
787+
.type_of(def_id)
788+
.no_bound_vars()
789+
.expect("statics should not have generic parameters");
790+
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
791+
assert!(layout.is_sized());
792+
(layout.size, layout.align.abi)
793+
};
794+
(size, align, AllocKind::LiveData)
777795
}
778796
Some(GlobalAlloc::Memory(alloc)) => {
779797
// Need to duplicate the logic here, because the global allocations have

compiler/rustc_const_eval/src/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in
2222

2323
pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
2424
pub use self::intern::{
25-
intern_const_alloc_for_constprop, intern_const_alloc_recursive, InternKind,
25+
intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind,
2626
};
2727
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
2828
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};

compiler/rustc_const_eval/src/interpret/util.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::const_eval::CompileTimeEvalContext;
22
use crate::interpret::{MemPlaceMeta, MemoryKind};
3+
use rustc_hir::def_id::LocalDefId;
34
use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer};
45
use rustc_middle::ty::layout::TyAndLayout;
56
use rustc_middle::ty::{
67
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
78
};
8-
use rustc_span::def_id::DefId;
99
use std::ops::ControlFlow;
1010

1111
use super::MPlaceTy;
@@ -89,13 +89,13 @@ pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>(
8989

9090
pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>(
9191
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
92-
static_def_id: DefId,
92+
static_def_id: LocalDefId,
9393
layout: TyAndLayout<'tcx>,
9494
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
9595
let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?;
96-
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id);
97-
assert_eq!(ecx.machine.static_root_alloc_id, None);
98-
ecx.machine.static_root_alloc_id = Some(alloc_id);
96+
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
97+
assert_eq!(ecx.machine.static_root_ids, None);
98+
ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
9999
assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
100100
Ok(ecx.ptr_with_meta_to_mplace(Pointer::from(alloc_id).into(), MemPlaceMeta::None, layout))
101101
}

compiler/rustc_const_eval/src/interpret/validity.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -457,16 +457,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
457457
// Special handling for pointers to statics (irrespective of their type).
458458
assert!(!self.ecx.tcx.is_thread_local_static(did));
459459
assert!(self.ecx.tcx.is_static(did));
460-
let is_mut = matches!(
461-
self.ecx.tcx.def_kind(did),
462-
DefKind::Static { mt: Mutability::Mut, .. }
463-
) || !self
464-
.ecx
465-
.tcx
466-
.type_of(did)
467-
.no_bound_vars()
468-
.expect("statics should not have generic parameters")
469-
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all());
470460
// Mode-specific checks
471461
match self.ctfe_mode {
472462
Some(
@@ -491,8 +481,26 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
491481
}
492482
None => {}
493483
}
494-
// Return alloc mutability
495-
if is_mut { Mutability::Mut } else { Mutability::Not }
484+
// Return alloc mutability. For "root" statics we look at the type to account for interior
485+
// mutability; for nested statics we have no type and directly use the annotated mutability.
486+
match self.ecx.tcx.def_kind(did) {
487+
DefKind::Static { mt: Mutability::Mut, .. } => Mutability::Mut,
488+
DefKind::Static { mt: Mutability::Not, nested: true } => {
489+
Mutability::Not
490+
}
491+
DefKind::Static { mt: Mutability::Not, nested: false }
492+
if !self
493+
.ecx
494+
.tcx
495+
.type_of(did)
496+
.no_bound_vars()
497+
.expect("statics should not have generic parameters")
498+
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) =>
499+
{
500+
Mutability::Mut
501+
}
502+
_ => Mutability::Not,
503+
}
496504
}
497505
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
498506
GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {

0 commit comments

Comments
 (0)