mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 22:34:05 +00:00
Ensure nested allocations in statics do not get deduplicated
This commit is contained in:
parent
92414ab25d
commit
d3514a036d
@ -1,7 +1,9 @@
|
||||
#[cfg(feature = "master")]
|
||||
use gccjit::{FnAttribute, VarAttribute};
|
||||
use rustc_codegen_ssa::traits::PreDefineMethods;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::mono::{Linkage, Visibility};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
|
||||
@ -23,7 +25,14 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
) {
|
||||
let attrs = self.tcx.codegen_fn_attrs(def_id);
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
|
||||
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
|
||||
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
|
||||
// the gcc type from the actual evaluated initializer.
|
||||
let ty = if nested {
|
||||
self.tcx.types.unit
|
||||
} else {
|
||||
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
|
||||
};
|
||||
let gcc_type = self.layout_of(ty).gcc_type(self);
|
||||
|
||||
let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
|
||||
|
@ -9,6 +9,7 @@ use crate::type_::Type;
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
use crate::value::Value;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
use rustc_middle::mir::interpret::{
|
||||
@ -229,9 +230,17 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
trace!(?instance);
|
||||
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
|
||||
trace!(?ty);
|
||||
let llty = self.layout_of(ty).llvm_type(self);
|
||||
|
||||
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
|
||||
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
|
||||
// the llvm type from the actual evaluated initializer.
|
||||
let llty = if nested {
|
||||
self.type_i8()
|
||||
} else {
|
||||
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
|
||||
trace!(?ty);
|
||||
self.layout_of(ty).llvm_type(self)
|
||||
};
|
||||
self.get_static_inner(def_id, llty)
|
||||
}
|
||||
|
||||
@ -346,6 +355,12 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||
|
||||
fn codegen_static_item(&self, def_id: DefId) {
|
||||
unsafe {
|
||||
assert!(
|
||||
llvm::LLVMGetInitializer(
|
||||
self.instances.borrow().get(&Instance::mono(self.tcx, def_id)).unwrap()
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
let attrs = self.tcx.codegen_fn_attrs(def_id);
|
||||
|
||||
let Ok((v, alloc)) = codegen_static_initializer(self, def_id) else {
|
||||
|
@ -26,6 +26,7 @@ use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_fs_util::path_to_c_string;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_middle::bug;
|
||||
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
|
||||
};
|
||||
|
||||
let is_local_to_unit = is_node_local_to_unit(cx, def_id);
|
||||
|
||||
let DefKind::Static { nested, .. } = cx.tcx.def_kind(def_id) else { bug!() };
|
||||
if nested {
|
||||
return;
|
||||
}
|
||||
let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all());
|
||||
let type_di_node = type_di_node(cx, variable_type);
|
||||
let var_name = tcx.item_name(def_id);
|
||||
|
@ -5,7 +5,9 @@ use crate::errors::SymbolAlreadyDefined;
|
||||
use crate::llvm;
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::mono::{Linkage, Visibility};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
|
||||
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
|
||||
@ -21,7 +23,14 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
symbol_name: &str,
|
||||
) {
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
|
||||
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
|
||||
// Nested statics do not have a type, so pick a random type and let `define_static` figure out
|
||||
// the llvm type from the actual evaluated initializer.
|
||||
let ty = if nested {
|
||||
self.tcx.types.unit
|
||||
} else {
|
||||
instance.ty(self.tcx, ty::ParamEnv::reveal_all())
|
||||
};
|
||||
let llty = self.layout_of(ty).llvm_type(self);
|
||||
|
||||
let g = self.define_global(symbol_name, llty).unwrap_or_else(|| {
|
||||
|
@ -59,7 +59,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
};
|
||||
|
||||
let ret = if let InternKind::Static(_) = intern_kind {
|
||||
create_static_alloc(ecx, cid.instance.def_id(), layout)?
|
||||
create_static_alloc(ecx, cid.instance.def_id().expect_local(), layout)?
|
||||
} else {
|
||||
ecx.allocate(layout, MemoryKind::Stack)?
|
||||
};
|
||||
@ -380,7 +380,11 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
|
||||
}
|
||||
Ok(mplace) => {
|
||||
// Since evaluation had no errors, validate the resulting constant.
|
||||
|
||||
// Temporarily allow access to the static_root_ids for the purpose of validation.
|
||||
let static_root_ids = ecx.machine.static_root_ids.take();
|
||||
let res = const_validate_mplace(&ecx, &mplace, cid);
|
||||
ecx.machine.static_root_ids = static_root_ids;
|
||||
|
||||
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
|
||||
|
||||
|
@ -8,6 +8,7 @@ use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::fx::IndexEntry;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::AssertMessage;
|
||||
@ -59,8 +60,10 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
|
||||
/// Whether to check alignment during evaluation.
|
||||
pub(super) check_alignment: CheckAlignment,
|
||||
|
||||
/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization.
|
||||
pub(crate) static_root_alloc_id: Option<AllocId>,
|
||||
/// If `Some`, we are evaluating the initializer of the static with the given `LocalDefId`,
|
||||
/// storing the result in the given `AllocId`.
|
||||
/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization loops.
|
||||
pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -94,7 +97,7 @@ impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
|
||||
stack: Vec::new(),
|
||||
can_access_mut_global,
|
||||
check_alignment,
|
||||
static_root_alloc_id: None,
|
||||
static_root_ids: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -749,7 +752,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
alloc_id: AllocId,
|
||||
) -> InterpResult<'tcx> {
|
||||
if Some(alloc_id) == ecx.machine.static_root_alloc_id {
|
||||
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
|
||||
Err(ConstEvalErrKind::RecursiveStatic.into())
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -13,12 +13,16 @@
|
||||
//! but that would require relying on type information, and given how many ways Rust has to lie
|
||||
//! about type information, we want to avoid doing that.
|
||||
|
||||
use hir::def::DefKind;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult};
|
||||
use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
|
||||
use rustc_middle::query::TyCtxtAt;
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
|
||||
use crate::const_eval;
|
||||
@ -33,7 +37,19 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
|
||||
FrameExtra = (),
|
||||
AllocExtra = (),
|
||||
MemoryMap = FxIndexMap<AllocId, (MemoryKind<T>, Allocation)>,
|
||||
>;
|
||||
> + HasStaticRootDefId;
|
||||
|
||||
pub trait HasStaticRootDefId {
|
||||
/// Returns the `DefId` of the static item that is currently being evaluated.
|
||||
/// Used for interning to be able to handle nested allocations.
|
||||
fn static_def_id(&self) -> Option<LocalDefId>;
|
||||
}
|
||||
|
||||
impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_, '_> {
|
||||
fn static_def_id(&self) -> Option<LocalDefId> {
|
||||
Some(self.static_root_ids?.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory.
|
||||
///
|
||||
@ -67,10 +83,35 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
|
||||
}
|
||||
// link the alloc id to the actual allocation
|
||||
let alloc = ecx.tcx.mk_const_alloc(alloc);
|
||||
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
if let Some(static_id) = ecx.machine.static_def_id() {
|
||||
intern_as_new_static(ecx.tcx, static_id, alloc_id, alloc);
|
||||
} else {
|
||||
ecx.tcx.set_alloc_id_memory(alloc_id, alloc);
|
||||
}
|
||||
Ok(alloc.0.0.provenance().ptrs().iter().map(|&(_, prov)| prov))
|
||||
}
|
||||
|
||||
/// Creates a new `DefId` and feeds all the right queries to make this `DefId`
|
||||
/// appear as if it were a user-written `static` (though it has no HIR).
|
||||
fn intern_as_new_static<'tcx>(
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
static_id: LocalDefId,
|
||||
alloc_id: AllocId,
|
||||
alloc: ConstAllocation<'tcx>,
|
||||
) {
|
||||
let feed = tcx.create_def(
|
||||
static_id,
|
||||
sym::nested,
|
||||
DefKind::Static { mt: alloc.0.mutability, nested: true },
|
||||
);
|
||||
tcx.set_nested_alloc_id_static(alloc_id, feed.def_id());
|
||||
feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone());
|
||||
feed.eval_static_initializer(Ok(alloc));
|
||||
feed.generics_of(tcx.generics_of(static_id).clone());
|
||||
feed.def_ident_span(tcx.def_ident_span(static_id));
|
||||
feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id));
|
||||
}
|
||||
|
||||
/// How a constant value should be interned.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
|
||||
pub enum InternKind {
|
||||
|
@ -15,6 +15,7 @@ use std::ptr;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::mir::display_allocation;
|
||||
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
@ -761,19 +762,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// be held throughout the match.
|
||||
match self.tcx.try_get_global_alloc(id) {
|
||||
Some(GlobalAlloc::Static(def_id)) => {
|
||||
assert!(self.tcx.is_static(def_id));
|
||||
// Thread-local statics do not have a constant address. They *must* be accessed via
|
||||
// `ThreadLocalRef`; we can never have a pointer to them as a regular constant value.
|
||||
assert!(!self.tcx.is_thread_local_static(def_id));
|
||||
// Use size and align of the type.
|
||||
let ty = self
|
||||
.tcx
|
||||
.type_of(def_id)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters");
|
||||
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
|
||||
assert!(layout.is_sized());
|
||||
(layout.size, layout.align.abi, AllocKind::LiveData)
|
||||
|
||||
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else {
|
||||
bug!("GlobalAlloc::Static is not a static")
|
||||
};
|
||||
|
||||
let (size, align) = if nested {
|
||||
// Nested anonymous statics are untyped, so let's get their
|
||||
// size and alignment from the allocaiton itself. This always
|
||||
// succeeds, as the query is fed at DefId creation time, so no
|
||||
// evaluation actually occurs.
|
||||
let alloc = self.tcx.eval_static_initializer(def_id).unwrap();
|
||||
(alloc.0.size(), alloc.0.align)
|
||||
} else {
|
||||
// Use size and align of the type for everything else. We need
|
||||
// to do that to
|
||||
// * avoid cycle errors in case of self-referential statics,
|
||||
// * be able to get information on extern statics.
|
||||
let ty = self
|
||||
.tcx
|
||||
.type_of(def_id)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters");
|
||||
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
|
||||
assert!(layout.is_sized());
|
||||
(layout.size, layout.align.abi)
|
||||
};
|
||||
(size, align, AllocKind::LiveData)
|
||||
}
|
||||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
// Need to duplicate the logic here, because the global allocations have
|
||||
|
@ -22,7 +22,7 @@ pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in
|
||||
|
||||
pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
|
||||
pub use self::intern::{
|
||||
intern_const_alloc_for_constprop, intern_const_alloc_recursive, InternKind,
|
||||
intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind,
|
||||
};
|
||||
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
|
||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::const_eval::CompileTimeEvalContext;
|
||||
use crate::interpret::{MemPlaceMeta, MemoryKind};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_span::def_id::DefId;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::MPlaceTy;
|
||||
@ -89,13 +89,13 @@ pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>(
|
||||
|
||||
pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>(
|
||||
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||
static_def_id: DefId,
|
||||
static_def_id: LocalDefId,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
|
||||
let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?;
|
||||
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id);
|
||||
assert_eq!(ecx.machine.static_root_alloc_id, None);
|
||||
ecx.machine.static_root_alloc_id = Some(alloc_id);
|
||||
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
|
||||
assert_eq!(ecx.machine.static_root_ids, None);
|
||||
ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
|
||||
assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
|
||||
Ok(ecx.ptr_with_meta_to_mplace(Pointer::from(alloc_id).into(), MemPlaceMeta::None, layout))
|
||||
}
|
||||
|
@ -457,16 +457,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
// Special handling for pointers to statics (irrespective of their type).
|
||||
assert!(!self.ecx.tcx.is_thread_local_static(did));
|
||||
assert!(self.ecx.tcx.is_static(did));
|
||||
let is_mut = matches!(
|
||||
self.ecx.tcx.def_kind(did),
|
||||
DefKind::Static { mt: Mutability::Mut, .. }
|
||||
) || !self
|
||||
.ecx
|
||||
.tcx
|
||||
.type_of(did)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters")
|
||||
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all());
|
||||
// Mode-specific checks
|
||||
match self.ctfe_mode {
|
||||
Some(
|
||||
@ -491,8 +481,26 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// Return alloc mutability
|
||||
if is_mut { Mutability::Mut } else { Mutability::Not }
|
||||
// Return alloc mutability. For "root" statics we look at the type to account for interior
|
||||
// mutability; for nested statics we have no type and directly use the annotated mutability.
|
||||
match self.ecx.tcx.def_kind(did) {
|
||||
DefKind::Static { mt: Mutability::Mut, .. } => Mutability::Mut,
|
||||
DefKind::Static { mt: Mutability::Not, nested: true } => {
|
||||
Mutability::Not
|
||||
}
|
||||
DefKind::Static { mt: Mutability::Not, nested: false }
|
||||
if !self
|
||||
.ecx
|
||||
.tcx
|
||||
.type_of(did)
|
||||
.no_bound_vars()
|
||||
.expect("statics should not have generic parameters")
|
||||
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) =>
|
||||
{
|
||||
Mutability::Mut
|
||||
}
|
||||
_ => Mutability::Not,
|
||||
}
|
||||
}
|
||||
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
|
||||
GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
|
||||
|
@ -250,6 +250,9 @@ impl DefKind {
|
||||
| DefKind::AssocTy
|
||||
| DefKind::TyParam
|
||||
| DefKind::ExternCrate => DefPathData::TypeNs(name),
|
||||
// It's not exactly an anon const, but wrt DefPathData, there
|
||||
// is not difference.
|
||||
DefKind::Static { nested: true, .. } => DefPathData::AnonConst,
|
||||
DefKind::Fn
|
||||
| DefKind::Const
|
||||
| DefKind::ConstParam
|
||||
|
@ -894,7 +894,7 @@ fn should_encode_attrs(def_kind: DefKind) -> bool {
|
||||
| DefKind::AssocTy
|
||||
| DefKind::Fn
|
||||
| DefKind::Const
|
||||
| DefKind::Static { .. }
|
||||
| DefKind::Static { nested: false, .. }
|
||||
| DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
| DefKind::Macro(_)
|
||||
@ -915,6 +915,7 @@ fn should_encode_attrs(def_kind: DefKind) -> bool {
|
||||
| DefKind::InlineConst
|
||||
| DefKind::OpaqueTy
|
||||
| DefKind::LifetimeParam
|
||||
| DefKind::Static { nested: true, .. }
|
||||
| DefKind::GlobalAsm => false,
|
||||
}
|
||||
}
|
||||
@ -968,7 +969,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool {
|
||||
| DefKind::AssocTy
|
||||
| DefKind::Fn
|
||||
| DefKind::Const
|
||||
| DefKind::Static { .. }
|
||||
| DefKind::Static { nested: false, .. }
|
||||
| DefKind::Ctor(..)
|
||||
| DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
@ -981,6 +982,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool {
|
||||
| DefKind::LifetimeParam
|
||||
| DefKind::AnonConst
|
||||
| DefKind::InlineConst
|
||||
| DefKind::Static { nested: true, .. }
|
||||
| DefKind::OpaqueTy
|
||||
| DefKind::GlobalAsm
|
||||
| DefKind::Impl { .. }
|
||||
@ -1163,7 +1165,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
|
||||
| DefKind::Field
|
||||
| DefKind::Fn
|
||||
| DefKind::Const
|
||||
| DefKind::Static { .. }
|
||||
| DefKind::Static { nested: false, .. }
|
||||
| DefKind::TyAlias
|
||||
| DefKind::ForeignTy
|
||||
| DefKind::Impl { .. }
|
||||
@ -1205,6 +1207,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
|
||||
| DefKind::Mod
|
||||
| DefKind::ForeignMod
|
||||
| DefKind::Macro(..)
|
||||
| DefKind::Static { nested: true, .. }
|
||||
| DefKind::Use
|
||||
| DefKind::LifetimeParam
|
||||
| DefKind::GlobalAsm
|
||||
|
@ -130,7 +130,7 @@ use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::{HashMapExt, Lock};
|
||||
use rustc_data_structures::tiny_list::TinyList;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
@ -627,6 +627,16 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Freezes an `AllocId` created with `reserve` by pointing it at a static item. Trying to
|
||||
/// call this function twice, even with the same `DefId` will ICE the compiler.
|
||||
pub fn set_nested_alloc_id_static(self, id: AllocId, def_id: LocalDefId) {
|
||||
if let Some(old) =
|
||||
self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Static(def_id.to_def_id()))
|
||||
{
|
||||
bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
|
||||
/// twice for the same `(AllocId, Allocation)` pair.
|
||||
fn set_alloc_id_same_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) {
|
||||
|
@ -1062,6 +1062,7 @@ rustc_queries! {
|
||||
}
|
||||
cache_on_disk_if { key.is_local() }
|
||||
separate_provide_extern
|
||||
feedable
|
||||
}
|
||||
|
||||
/// Evaluates const items or anonymous constants
|
||||
@ -1220,6 +1221,7 @@ rustc_queries! {
|
||||
arena_cache
|
||||
cache_on_disk_if { def_id.is_local() }
|
||||
separate_provide_extern
|
||||
feedable
|
||||
}
|
||||
|
||||
query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! Currently, this pass only propagates scalar values.
|
||||
|
||||
use rustc_const_eval::interpret::{
|
||||
ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable,
|
||||
HasStaticRootDefId, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable,
|
||||
};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -889,6 +889,12 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
|
||||
|
||||
pub(crate) struct DummyMachine;
|
||||
|
||||
impl HasStaticRootDefId for DummyMachine {
|
||||
fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine {
|
||||
rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>);
|
||||
type MemoryKind = !;
|
||||
|
@ -380,8 +380,12 @@ fn collect_items_rec<'tcx>(
|
||||
// Sanity check whether this ended up being collected accidentally
|
||||
debug_assert!(should_codegen_locally(tcx, &instance));
|
||||
|
||||
let ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
|
||||
visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items);
|
||||
let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() };
|
||||
// Nested statics have no type.
|
||||
if !nested {
|
||||
let ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
|
||||
visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items);
|
||||
}
|
||||
|
||||
recursion_depth_reset = None;
|
||||
|
||||
|
@ -13,6 +13,7 @@ use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
use rustc_middle::middle::privacy::{self, Level};
|
||||
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::config::CrateType;
|
||||
@ -197,10 +198,23 @@ impl<'tcx> ReachableContext<'tcx> {
|
||||
// Reachable constants will be inlined into other crates
|
||||
// unconditionally, so we need to make sure that their
|
||||
// contents are also reachable.
|
||||
hir::ItemKind::Const(_, _, init) | hir::ItemKind::Static(_, _, init) => {
|
||||
hir::ItemKind::Const(_, _, init) => {
|
||||
self.visit_nested_body(init);
|
||||
}
|
||||
|
||||
// Reachable statics are inlined if read from another constant or static
|
||||
// in other crates. Additionally anonymous nested statics may be created
|
||||
// when evaluating a static, so preserve those, too.
|
||||
hir::ItemKind::Static(_, _, init) => {
|
||||
// FIXME(oli-obk): remove this body walking and instead walk the evaluated initializer
|
||||
// to find nested items that end up in the final value instead of also marking symbols
|
||||
// as reachable that are only needed for evaluation.
|
||||
self.visit_nested_body(init);
|
||||
if let Ok(alloc) = self.tcx.eval_static_initializer(item.owner_id.def_id) {
|
||||
self.propagate_statics_from_alloc(item.owner_id.def_id, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
// These are normal, nothing reachable about these
|
||||
// inherently and their children are already in the
|
||||
// worklist, as determined by the privacy pass
|
||||
@ -266,6 +280,29 @@ impl<'tcx> ReachableContext<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds anonymous nested statics created for nested allocations and adds them to `reachable_symbols`.
|
||||
fn propagate_statics_from_alloc(&mut self, root: LocalDefId, alloc: ConstAllocation<'tcx>) {
|
||||
if !self.any_library {
|
||||
return;
|
||||
}
|
||||
for (_, prov) in alloc.0.provenance().ptrs().iter() {
|
||||
match self.tcx.global_alloc(prov.alloc_id()) {
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
if let Some(def_id) = def_id.as_local()
|
||||
&& self.tcx.local_parent(def_id) == root
|
||||
// This is the main purpose of this function: add the def_id we find
|
||||
// to `reachable_symbols`.
|
||||
&& self.reachable_symbols.insert(def_id)
|
||||
&& let Ok(alloc) = self.tcx.eval_static_initializer(def_id)
|
||||
{
|
||||
self.propagate_statics_from_alloc(root, alloc);
|
||||
}
|
||||
}
|
||||
GlobalAlloc::Function(_) | GlobalAlloc::VTable(_, _) | GlobalAlloc::Memory(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item<'tcx>(
|
||||
|
@ -1205,6 +1205,7 @@ symbols! {
|
||||
negative_bounds,
|
||||
negative_impls,
|
||||
neon,
|
||||
nested,
|
||||
never,
|
||||
never_patterns,
|
||||
never_type,
|
||||
|
@ -4,8 +4,8 @@
|
||||
#![feature(const_mut_refs)]
|
||||
|
||||
//! Regression test for https://github.com/rust-lang/rust/issues/79738
|
||||
//! Show how we are duplicationg allocations, even though static items that
|
||||
//! copy their value from another static should not also be duplicating
|
||||
//! Show how we are not duplicating allocations anymore. Statics that
|
||||
//! copy their value from another static used to also duplicate
|
||||
//! memory behind references.
|
||||
|
||||
extern crate const_mut_refs_crate as other;
|
||||
@ -21,15 +21,17 @@ pub static mut COPY_OF_REMOTE_FOO: &'static mut i32 = unsafe { FOO };
|
||||
|
||||
static DOUBLE_REF: &&i32 = &&99;
|
||||
static ONE_STEP_ABOVE: &i32 = *DOUBLE_REF;
|
||||
static mut DOUBLE_REF_MUT: &mut &mut i32 = &mut &mut 99;
|
||||
static mut ONE_STEP_ABOVE_MUT: &mut i32 = unsafe { *DOUBLE_REF_MUT };
|
||||
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
assert_ne!(FOO as *const i32, BAR as *const i32);
|
||||
assert_eq!(FOO as *const i32, BAR as *const i32);
|
||||
assert_eq!(INNER_MOD_FOO as *const i32, INNER_MOD_BAR as *const i32);
|
||||
assert_eq!(LOCAL_FOO as *const i32, LOCAL_BAR as *const i32);
|
||||
assert_eq!(*DOUBLE_REF as *const i32, ONE_STEP_ABOVE as *const i32);
|
||||
assert_eq!(*DOUBLE_REF_MUT as *mut i32, ONE_STEP_ABOVE_MUT as *mut i32);
|
||||
|
||||
// bug!
|
||||
assert_ne!(FOO as *const i32, COPY_OF_REMOTE_FOO as *const i32);
|
||||
assert_eq!(FOO as *const i32, COPY_OF_REMOTE_FOO as *const i32);
|
||||
}
|
||||
}
|
||||
|
24
tests/ui/statics/nested_struct.rs
Normal file
24
tests/ui/statics/nested_struct.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//@ check-pass
|
||||
/// oli-obk added this test after messing up the interner logic
|
||||
/// around mutability of nested allocations. This was not caught
|
||||
/// by the test suite, but by trying to build stage2 rustc.
|
||||
/// There is no real explanation for this test, as it was just
|
||||
/// a bug during a refactoring.
|
||||
|
||||
pub struct Lint {
|
||||
pub name: &'static str,
|
||||
pub desc: &'static str,
|
||||
pub report_in_external_macro: bool,
|
||||
pub is_loaded: bool,
|
||||
pub crate_level_only: bool,
|
||||
}
|
||||
|
||||
static FOO: &Lint = &Lint {
|
||||
name: &"foo",
|
||||
desc: "desc",
|
||||
report_in_external_macro: false,
|
||||
is_loaded: true,
|
||||
crate_level_only: false,
|
||||
};
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user