Auto merge of #121644 - oli-obk:unique_static_innards2, r=RalfJung,nnethercote

Ensure nested allocations in statics neither get deduplicated nor duplicated

This PR generates new `DefId`s for nested allocations in static items and feeds all the right queries to make the compiler believe these are regular `static` items. I chose this design, because all other designs are fragile and make the compiler horribly complex for such a niche use case.

At present this wrecks incremental compilation performance *in case nested allocations exist* (because any query creating a `DefId` will be recomputed and never loaded from the cache). This will be resolved later in https://github.com/rust-lang/rust/pull/115613 . All other statics are unaffected by this change and will not have performance regressions (heh, famous last words)

This PR contains various smaller refactorings that can be pulled out into separate PRs. It is best reviewed commit-by-commit. The last commit is where the actual magic happens.

r? `@RalfJung` on the const interner and engine changes

fixes https://github.com/rust-lang/rust/issues/79738
This commit is contained in:
bors 2024-03-12 10:29:15 +00:00
commit 3b85d2c7fc
66 changed files with 461 additions and 170 deletions

View File

@ -196,7 +196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.get_partial_res(sym.id)
.and_then(|res| res.full_res())
.and_then(|res| match res {
Res::Def(DefKind::Static(_), def_id) => Some(def_id),
Res::Def(DefKind::Static { .. }, def_id) => Some(def_id),
_ => None,
});

View File

@ -63,7 +63,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
global_value
}
fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
fn codegen_static(&self, def_id: DefId) {
let attrs = self.tcx.codegen_fn_attrs(def_id);
let value = match codegen_static_initializer(&self, def_id) {
@ -92,7 +92,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
// As an optimization, all shared statics which do not have interior
// mutability are placed into read-only memory.
if !is_mutable && self.type_is_freeze(ty) {
if !self.tcx.static_mutability(def_id).unwrap().is_mut() && self.type_is_freeze(ty) {
#[cfg(feature = "master")]
global.global_set_readonly();
}
@ -349,7 +349,7 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>(
cx.const_struct(&llvals, true)
}
pub fn codegen_static_initializer<'gcc, 'tcx>(
fn codegen_static_initializer<'gcc, 'tcx>(
cx: &CodegenCx<'gcc, 'tcx>,
def_id: DefId,
) -> Result<(RValue<'gcc>, ConstAllocation<'tcx>), ErrorHandled> {

View File

@ -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 dummy type and let `codegen_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);

View File

@ -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::{
@ -17,7 +18,7 @@ use rustc_middle::mir::interpret::{
};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::ty::{self, Instance};
use rustc_middle::{bug, span_bug};
use rustc_session::config::Lto;
use rustc_target::abi::{
@ -114,7 +115,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
cx.const_struct(&llvals, true)
}
pub fn codegen_static_initializer<'ll, 'tcx>(
fn codegen_static_initializer<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
def_id: DefId,
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
@ -147,11 +148,10 @@ fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align:
fn check_and_apply_linkage<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
attrs: &CodegenFnAttrs,
ty: Ty<'tcx>,
llty: &'ll Type,
sym: &str,
def_id: DefId,
) -> &'ll Value {
let llty = cx.layout_of(ty).llvm_type(cx);
if let Some(linkage) = attrs.import_linkage {
debug!("get_static: sym={} linkage={:?}", sym, linkage);
@ -226,9 +226,28 @@ impl<'ll> CodegenCx<'ll, '_> {
}
}
#[instrument(level = "debug", skip(self))]
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
let instance = Instance::mono(self.tcx, def_id);
if let Some(&g) = self.instances.borrow().get(&instance) {
trace!(?instance);
let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() };
// Nested statics do not have a type, so pick a dummy type and let `codegen_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)
}
#[instrument(level = "debug", skip(self, llty))]
pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value {
if let Some(&g) = self.instances.borrow().get(&Instance::mono(self.tcx, def_id)) {
trace!("used cached value");
return g;
}
@ -240,14 +259,12 @@ impl<'ll> CodegenCx<'ll, '_> {
statics defined in the same CGU, but did not for `{def_id:?}`"
);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let sym = self.tcx.symbol_name(instance).name;
let sym = self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name;
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
debug!("get_static: sym={} instance={:?} fn_attrs={:?}", sym, instance, fn_attrs);
debug!(?sym, ?fn_attrs);
let g = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) {
let llty = self.layout_of(ty).llvm_type(self);
if let Some(g) = self.get_declared_value(sym) {
if self.val_ty(g) != self.type_ptr() {
span_bug!(self.tcx.def_span(def_id), "Conflicting types for static");
@ -264,7 +281,7 @@ impl<'ll> CodegenCx<'ll, '_> {
g
} else {
check_and_apply_linkage(self, fn_attrs, ty, sym, def_id)
check_and_apply_linkage(self, fn_attrs, llty, sym, def_id)
};
// Thread-local statics in some other crate need to *always* be linked
@ -332,34 +349,18 @@ impl<'ll> CodegenCx<'ll, '_> {
}
}
self.instances.borrow_mut().insert(instance, g);
self.instances.borrow_mut().insert(Instance::mono(self.tcx, def_id), g);
g
}
}
impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value {
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
unsafe {
// Upgrade the alignment in cases where the same constant is used with different
// alignment requirements
let llalign = align.bytes() as u32;
if llalign > llvm::LLVMGetAlignment(gv) {
llvm::LLVMSetAlignment(gv, llalign);
}
}
return gv;
}
let gv = self.static_addr_of_mut(cv, align, kind);
unsafe {
llvm::LLVMSetGlobalConstant(gv, True);
}
self.const_globals.borrow_mut().insert(cv, gv);
gv
}
fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
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 {
@ -368,13 +369,11 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
};
let alloc = alloc.inner();
let g = self.get_static(def_id);
let val_llty = self.val_ty(v);
let instance = Instance::mono(self.tcx, def_id);
let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
let llty = self.layout_of(ty).llvm_type(self);
let g = self.get_static_inner(def_id, val_llty);
let llty = self.val_ty(g);
let g = if val_llty == llty {
g
} else {
@ -409,16 +408,15 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
self.statics_to_rauw.borrow_mut().push((g, new_g));
new_g
};
set_global_alignment(self, g, self.align_of(ty));
set_global_alignment(self, g, alloc.align);
llvm::LLVMSetInitializer(g, v);
if self.should_assume_dso_local(g, true) {
llvm::LLVMRustSetDSOLocal(g, true);
}
// As an optimization, all shared statics which do not have interior
// mutability are placed into read-only memory.
if !is_mutable && self.type_is_freeze(ty) {
// Forward the allocation's mutability (picked by the const interner) to LLVM.
if alloc.mutability.is_not() {
llvm::LLVMSetGlobalConstant(g, llvm::True);
}
@ -541,6 +539,32 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
}
}
}
}
impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value {
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
unsafe {
// Upgrade the alignment in cases where the same constant is used with different
// alignment requirements
let llalign = align.bytes() as u32;
if llalign > llvm::LLVMGetAlignment(gv) {
llvm::LLVMSetAlignment(gv, llalign);
}
}
return gv;
}
let gv = self.static_addr_of_mut(cv, align, kind);
unsafe {
llvm::LLVMSetGlobalConstant(gv, True);
}
self.const_globals.borrow_mut().insert(cv, gv);
gv
}
fn codegen_static(&self, def_id: DefId) {
self.codegen_static_item(def_id)
}
/// Add a global value to a list to be stored in the `llvm.used` variable, an array of ptr.
fn add_used_global(&self, global: &'ll Value) {

View File

@ -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);

View File

@ -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 dummy type and let `codegen_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(|| {

View File

@ -87,7 +87,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
// Only consider nodes that actually have exported symbols.
match tcx.def_kind(def_id) {
DefKind::Fn | DefKind::Static(_) => {}
DefKind::Fn | DefKind::Static { .. } => {}
DefKind::AssocFn if tcx.impl_of_method(def_id.to_def_id()).is_some() => {}
_ => return None,
};
@ -483,7 +483,7 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel
let target = &tcx.sess.target.llvm_target;
// WebAssembly cannot export data symbols, so reduce their export level
if target.contains("emscripten") {
if let DefKind::Static(_) = tcx.def_kind(sym_def_id) {
if let DefKind::Static { .. } = tcx.def_kind(sym_def_id) {
return SymbolExportLevel::Rust;
}
}

View File

@ -30,7 +30,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
match *self {
MonoItem::Static(def_id) => {
cx.codegen_static(def_id, cx.tcx().is_mutable_static(def_id));
cx.codegen_static(def_id);
}
MonoItem::GlobalAsm(item_id) => {
let item = cx.tcx().hir().item(item_id);

View File

@ -4,7 +4,7 @@ use rustc_target::abi::Align;
pub trait StaticMethods: BackendTypes {
fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value;
fn codegen_static(&self, def_id: DefId, is_mutable: bool);
fn codegen_static(&self, def_id: DefId);
/// Mark the given global value as "used", to prevent the compiler and linker from potentially
/// removing a static variable that may otherwise appear unused.

View File

@ -37,7 +37,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|| matches!(
ecx.tcx.def_kind(cid.instance.def_id()),
DefKind::Const
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::ConstParam
| DefKind::AnonConst
| DefKind::InlineConst
@ -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();

View File

@ -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(())

View File

@ -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 { mutability: 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 {

View File

@ -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

View File

@ -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};

View File

@ -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))
}

View File

@ -457,15 +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(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(
@ -490,8 +481,28 @@ 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.
let DefKind::Static { mutability, nested } = self.ecx.tcx.def_kind(did)
else {
bug!()
};
match (mutability, nested) {
(Mutability::Mut, _) => Mutability::Mut,
(Mutability::Not, true) => Mutability::Not,
(Mutability::Not, 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, false) => Mutability::Not,
}
}
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {

View File

@ -75,7 +75,12 @@ pub enum DefKind {
Const,
/// Constant generic parameter: `struct Foo<const N: usize> { ... }`
ConstParam,
Static(ast::Mutability),
Static {
/// Whether it's a `static mut` or just a `static`.
mutability: ast::Mutability,
/// Whether it's an anonymous static generated for nested allocations.
nested: bool,
},
/// Refers to the struct or enum variant's constructor.
///
/// The reason `Ctor` exists in addition to [`DefKind::Struct`] and
@ -136,7 +141,7 @@ impl DefKind {
DefKind::Fn => "function",
DefKind::Mod if def_id.is_crate_root() && !def_id.is_local() => "crate",
DefKind::Mod => "module",
DefKind::Static(..) => "static",
DefKind::Static { .. } => "static",
DefKind::Enum => "enum",
DefKind::Variant => "variant",
DefKind::Ctor(CtorOf::Variant, CtorKind::Fn) => "tuple variant",
@ -209,7 +214,7 @@ impl DefKind {
DefKind::Fn
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::Ctor(..)
| DefKind::AssocFn
| DefKind::AssocConst => Some(Namespace::ValueNS),
@ -245,10 +250,13 @@ impl DefKind {
| DefKind::AssocTy
| DefKind::TyParam
| DefKind::ExternCrate => DefPathData::TypeNs(name),
// It's not exactly an anon const, but wrt DefPathData, there
// is no difference.
DefKind::Static { nested: true, .. } => DefPathData::AnonConst,
DefKind::Fn
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::AssocFn
| DefKind::AssocConst
| DefKind::Field => DefPathData::ValueNs(name),
@ -278,7 +286,7 @@ impl DefKind {
| DefKind::AssocFn
| DefKind::Ctor(..)
| DefKind::Closure
| DefKind::Static(_) => true,
| DefKind::Static { .. } => true,
DefKind::Mod
| DefKind::Struct
| DefKind::Union

View File

@ -1616,7 +1616,7 @@ impl Expr<'_> {
pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool {
match self.kind {
ExprKind::Path(QPath::Resolved(_, ref path)) => {
matches!(path.res, Res::Local(..) | Res::Def(DefKind::Static(_), _) | Res::Err)
matches!(path.res, Res::Local(..) | Res::Def(DefKind::Static { .. }, _) | Res::Err)
}
// Type ascription inherits its place expression kind from its

View File

@ -107,7 +107,7 @@ impl Target {
match item.kind {
ItemKind::ExternCrate(..) => Target::ExternCrate,
ItemKind::Use(..) => Target::Use,
ItemKind::Static(..) => Target::Static,
ItemKind::Static { .. } => Target::Static,
ItemKind::Const(..) => Target::Const,
ItemKind::Fn(..) => Target::Fn,
ItemKind::Macro(..) => Target::MacroDef,
@ -130,7 +130,7 @@ impl Target {
match def_kind {
DefKind::ExternCrate => Target::ExternCrate,
DefKind::Use => Target::Use,
DefKind::Static(..) => Target::Static,
DefKind::Static { .. } => Target::Static,
DefKind::Const => Target::Const,
DefKind::Fn => Target::Fn,
DefKind::Macro(..) => Target::MacroDef,

View File

@ -1934,7 +1934,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
// Case 3. Reference to a top-level value.
DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static(_) => {
DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } => {
path_segs.push(PathSeg(def_id, last));
}

View File

@ -226,7 +226,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
Ok(l) => l,
// Foreign statics that overflow their allowed size should emit an error
Err(LayoutError::SizeOverflow(_))
if matches!(tcx.def_kind(def_id), DefKind::Static(_)
if matches!(tcx.def_kind(def_id), DefKind::Static{ .. }
if tcx.def_kind(tcx.local_parent(def_id)) == DefKind::ForeignMod) =>
{
tcx.dcx().emit_err(errors::TooLargeStatic { span });
@ -505,7 +505,7 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) {
pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let _indenter = indenter();
match tcx.def_kind(def_id) {
DefKind::Static(..) => {
DefKind::Static { .. } => {
tcx.ensure().typeck(def_id);
maybe_check_static_with_link_section(tcx, def_id);
check_static_inhabited(tcx, def_id);

View File

@ -48,8 +48,7 @@ fn is_path_static_mut(expr: hir::Expr<'_>) -> Option<String> {
if let hir::ExprKind::Path(qpath) = expr.kind
&& let hir::QPath::Resolved(_, path) = qpath
&& let hir::def::Res::Def(def_kind, _) = path.res
&& let hir::def::DefKind::Static(mt) = def_kind
&& matches!(mt, Mutability::Mut)
&& let hir::def::DefKind::Static { mutability: Mutability::Mut, nested: false } = def_kind
{
return Some(qpath_to_string(&qpath));
}

View File

@ -182,7 +182,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
tcx.hir().par_body_owners(|item_def_id| {
let def_kind = tcx.def_kind(item_def_id);
match def_kind {
DefKind::Static(_) => tcx.ensure().eval_static_initializer(item_def_id),
DefKind::Static { .. } => tcx.ensure().eval_static_initializer(item_def_id),
DefKind::Const => tcx.ensure().const_eval_poly(item_def_id.into()),
_ => (),
}

View File

@ -699,7 +699,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::Path {
res:
hir::def::Res::Def(
hir::def::DefKind::Static(_) | hir::def::DefKind::Const,
hir::def::DefKind::Static { .. } | hir::def::DefKind::Const,
def_id,
),
..

View File

@ -398,7 +398,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
)
| Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, expr_ty)),
Res::Def(DefKind::Static(_), _) => {
Res::Def(DefKind::Static { .. }, _) => {
Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new()))
}

View File

@ -372,7 +372,7 @@ impl<T> Trait<T> for X {
&& matches!(
tcx.def_kind(body_owner_def_id),
DefKind::Fn
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Const
| DefKind::AssocFn
| DefKind::AssocConst

View File

@ -863,7 +863,7 @@ fn should_encode_span(def_kind: DefKind) -> bool {
| DefKind::LifetimeParam
| DefKind::Fn
| DefKind::Const
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Ctor(..)
| DefKind::AssocFn
| DefKind::AssocConst
@ -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,
}
}
@ -936,7 +937,7 @@ fn should_encode_expn_that_defined(def_kind: DefKind) -> bool {
| DefKind::Fn
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Ctor(..)
| DefKind::AssocFn
| DefKind::AssocConst
@ -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 { .. }
@ -1001,7 +1003,7 @@ fn should_encode_stability(def_kind: DefKind) -> bool {
| DefKind::AssocConst
| DefKind::TyParam
| DefKind::ConstParam
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::Const
| DefKind::Fn
| DefKind::ForeignMod
@ -1099,7 +1101,7 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def
| DefKind::AssocConst
| DefKind::TyParam
| DefKind::ConstParam
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::Const
| DefKind::ForeignMod
| DefKind::Impl { .. }
@ -1131,7 +1133,7 @@ fn should_encode_generics(def_kind: DefKind) -> bool {
| DefKind::AssocTy
| DefKind::Fn
| DefKind::Const
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::Ctor(..)
| DefKind::AssocFn
| DefKind::AssocConst
@ -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
@ -1222,7 +1225,7 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool {
| DefKind::Variant
| DefKind::Field
| DefKind::Const
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::Ctor(..)
| DefKind::TyAlias
| DefKind::OpaqueTy
@ -1263,7 +1266,7 @@ fn should_encode_constness(def_kind: DefKind) -> bool {
| DefKind::Const
| DefKind::AssocConst
| DefKind::AnonConst
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::TyAlias
| DefKind::OpaqueTy
| DefKind::Impl { of_trait: false }
@ -1295,7 +1298,7 @@ fn should_encode_const(def_kind: DefKind) -> bool {
| DefKind::Ctor(..)
| DefKind::Field
| DefKind::Fn
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::TyAlias
| DefKind::OpaqueTy
| DefKind::ForeignTy
@ -1469,7 +1472,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
.coroutine_for_closure
.set_some(def_id.index, self.tcx.coroutine_for_closure(def_id).into());
}
if let DefKind::Static(_) = def_kind {
if let DefKind::Static { .. } = def_kind {
if !self.tcx.is_foreign_item(def_id) {
let data = self.tcx.eval_static_initializer(def_id).unwrap();
record!(self.tables.eval_static_initializer[def_id] <- data);

View File

@ -155,8 +155,10 @@ fixed_size_enum! {
( Impl { of_trait: false } )
( Impl { of_trait: true } )
( Closure )
( Static(ast::Mutability::Not) )
( Static(ast::Mutability::Mut) )
( Static { mutability: ast::Mutability::Not, nested: false } )
( Static { mutability: ast::Mutability::Mut, nested: false } )
( Static { mutability: ast::Mutability::Not, nested: true } )
( Static { mutability: ast::Mutability::Mut, nested: true } )
( Ctor(CtorOf::Struct, CtorKind::Fn) )
( Ctor(CtorOf::Struct, CtorKind::Const) )
( Ctor(CtorOf::Variant, CtorKind::Fn) )

View File

@ -343,7 +343,7 @@ impl<'hir> Map<'hir> {
DefKind::InlineConst => BodyOwnerKind::Const { inline: true },
DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => BodyOwnerKind::Fn,
DefKind::Closure => BodyOwnerKind::Closure,
DefKind::Static(mt) => BodyOwnerKind::Static(mt),
DefKind::Static { mutability, nested: false } => BodyOwnerKind::Static(mutability),
dk => bug!("{:?} is not a body node: {:?}", def_id, dk),
}
}
@ -359,7 +359,7 @@ impl<'hir> Map<'hir> {
let def_id = def_id.into();
let ccx = match self.body_owner_kind(def_id) {
BodyOwnerKind::Const { inline } => ConstContext::Const { inline },
BodyOwnerKind::Static(mt) => ConstContext::Static(mt),
BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability),
BodyOwnerKind::Fn if self.tcx.is_constructor(def_id) => return None,
BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.tcx.is_const_fn_raw(def_id) => {

View File

@ -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>) {

View File

@ -498,8 +498,12 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io:
match (kind, body.source.promoted) {
(_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts
(DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
(DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
(DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
(DefKind::Static { mutability: hir::Mutability::Not, nested: false }, _) => {
write!(w, "static ")?
}
(DefKind::Static { mutability: hir::Mutability::Mut, nested: false }, _) => {
write!(w, "static mut ")?
}
(_, _) if is_function => write!(w, "fn ")?,
(DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
_ => bug!("Unexpected def kind {:?}", kind),

View File

@ -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> {

View File

@ -1708,7 +1708,7 @@ impl<'tcx> TyCtxt<'tcx> {
debug!("returned from def_kind: {:?}", def_kind);
match def_kind {
DefKind::Const
| DefKind::Static(..)
| DefKind::Static { .. }
| DefKind::AssocConst
| DefKind::Ctor(..)
| DefKind::AnonConst

View File

@ -359,7 +359,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
| DefKind::TyAlias
| DefKind::Fn
| DefKind::Const
| DefKind::Static(_) = kind
| DefKind::Static { .. } = kind
{
} else {
// If not covered above, like for example items out of `impl` blocks, fallback.

View File

@ -616,12 +616,16 @@ impl<'tcx> TyCtxt<'tcx> {
/// Returns `true` if the node pointed to by `def_id` is a `static` item.
#[inline]
pub fn is_static(self, def_id: DefId) -> bool {
matches!(self.def_kind(def_id), DefKind::Static(_))
matches!(self.def_kind(def_id), DefKind::Static { .. })
}
#[inline]
pub fn static_mutability(self, def_id: DefId) -> Option<hir::Mutability> {
if let DefKind::Static(mt) = self.def_kind(def_id) { Some(mt) } else { None }
if let DefKind::Static { mutability, .. } = self.def_kind(def_id) {
Some(mutability)
} else {
None
}
}
/// Returns `true` if this is a `static` item with the `#[thread_local]` attribute.

View File

@ -631,7 +631,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
| DefKind::AssocConst
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Static(_) => (vec![], tcx.type_of(def_id).instantiate_identity(), None),
| DefKind::Static { .. } => (vec![], tcx.type_of(def_id).instantiate_identity(), None),
DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => {
let sig = tcx.liberate_late_bound_regions(
def_id.to_def_id(),

View File

@ -942,7 +942,7 @@ impl<'tcx> Cx<'tcx> {
// We encode uses of statics as a `*&STATIC` where the `&STATIC` part is
// a constant reference (or constant raw pointer for `static mut`) in MIR
Res::Def(DefKind::Static(_), id) => {
Res::Def(DefKind::Static { .. }, id) => {
let ty = self.tcx.static_ptr_ty(id);
let temp_lifetime = self
.rvalue_scopes

View File

@ -453,7 +453,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
Res::Def(DefKind::ConstParam, _) => {
self.tcx.dcx().emit_err(ConstParamInPattern { span })
}
Res::Def(DefKind::Static(_), _) => {
Res::Def(DefKind::Static { .. }, _) => {
self.tcx.dcx().emit_err(StaticInPattern { span })
}
_ => self.tcx.dcx().emit_err(NonConstPath { span }),

View File

@ -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 = !;

View File

@ -333,7 +333,7 @@ fn mir_promoted(
}
DefKind::AssocConst
| DefKind::Const
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::InlineConst
| DefKind::AnonConst => tcx.mir_const_qualif(def),
_ => ConstQualifs::default(),

View File

@ -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;
@ -1037,7 +1041,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
return false;
}
if let DefKind::Static(_) = tcx.def_kind(def_id) {
if let DefKind::Static { .. } = tcx.def_kind(def_id) {
// We cannot monomorphize statics from upstream crates.
return false;
}
@ -1254,7 +1258,7 @@ impl<'v> RootCollector<'_, 'v> {
);
self.output.push(dummy_spanned(MonoItem::GlobalAsm(id)));
}
DefKind::Static(..) => {
DefKind::Static { .. } => {
let def_id = id.owner_id.to_def_id();
debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id));
self.output.push(dummy_spanned(MonoItem::Static(def_id)));

View File

@ -150,7 +150,7 @@ fn mark_used_by_default_parameters<'tcx>(
| DefKind::Fn
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Ctor(_, _)
| DefKind::AssocFn
| DefKind::AssocConst

View File

@ -801,7 +801,7 @@ fn check_foreign_item(
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
id: hir::ForeignItemId,
) {
if matches!(tcx.def_kind(id.owner_id), DefKind::Static(_) | DefKind::Fn)
if matches!(tcx.def_kind(id.owner_id), DefKind::Static { .. } | DefKind::Fn)
&& let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
{
worklist.push((id.owner_id.def_id, comes_from_allow));
@ -1058,7 +1058,7 @@ impl<'tcx> DeadVisitor<'tcx> {
DefKind::AssocConst
| DefKind::AssocFn
| DefKind::Fn
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Const
| DefKind::TyAlias
| DefKind::Enum

View File

@ -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;
@ -73,7 +74,7 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
match res {
// Reachable constants and reachable statics can have their contents inlined
// into other crates. Mark them as reachable and recurse into their body.
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::Static(_), _) => {
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::Static { .. }, _) => {
self.worklist.push(def_id);
}
_ => {
@ -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>(

View File

@ -549,7 +549,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
self.update(def_id, macro_ev, Level::Reachable);
match def_kind {
// No type privacy, so can be directly marked as reachable.
DefKind::Const | DefKind::Static(_) | DefKind::TraitAlias | DefKind::TyAlias => {
DefKind::Const | DefKind::Static { .. } | DefKind::TraitAlias | DefKind::TyAlias => {
if vis.is_accessible_from(module, self.tcx) {
self.update(def_id, macro_ev, Level::Reachable);
}
@ -1170,12 +1170,12 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
let def = def.filter(|(kind, _)| {
matches!(
kind,
DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static(_)
DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static { .. }
)
});
if let Some((kind, def_id)) = def {
let is_local_static =
if let DefKind::Static(_) = kind { def_id.is_local() } else { false };
if let DefKind::Static { .. } = kind { def_id.is_local() } else { false };
if !self.item_is_accessible(def_id) && !is_local_static {
let name = match *qpath {
hir::QPath::LangItem(it, ..) => {
@ -1496,7 +1496,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> {
let def_kind = tcx.def_kind(def_id);
match def_kind {
DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => {
DefKind::Const | DefKind::Static { .. } | DefKind::Fn | DefKind::TyAlias => {
if let DefKind::TyAlias = def_kind {
self.check_unnameable(def_id, effective_vis);
}

View File

@ -990,7 +990,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
Res::Def(
DefKind::Fn
| DefKind::AssocFn
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Const
| DefKind::AssocConst
| DefKind::Ctor(..),

View File

@ -127,7 +127,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
ItemKind::Union(..) => DefKind::Union,
ItemKind::ExternCrate(..) => DefKind::ExternCrate,
ItemKind::TyAlias(..) => DefKind::TyAlias,
ItemKind::Static(s) => DefKind::Static(s.mutability),
ItemKind::Static(s) => DefKind::Static { mutability: s.mutability, nested: false },
ItemKind::Const(..) => DefKind::Const,
ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn,
ItemKind::MacroDef(..) => {
@ -214,7 +214,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
let def_kind = match fi.kind {
ForeignItemKind::Static(_, mt, _) => DefKind::Static(mt),
ForeignItemKind::Static(_, mutability, _) => {
DefKind::Static { mutability, nested: false }
}
ForeignItemKind::Fn(_) => DefKind::Fn,
ForeignItemKind::TyAlias(_) => DefKind::ForeignTy,
ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id),

View File

@ -574,7 +574,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ResolutionError::GenericParamsFromOuterItem(outer_res, has_generic_params, def_kind) => {
use errs::GenericParamsFromOuterItemLabel as Label;
let static_or_const = match def_kind {
DefKind::Static(_) => Some(errs::GenericParamsFromOuterItemStaticOrConst::Static),
DefKind::Static{ .. } => Some(errs::GenericParamsFromOuterItemStaticOrConst::Static),
DefKind::Const => Some(errs::GenericParamsFromOuterItemStaticOrConst::Const),
_ => None,
};

View File

@ -500,7 +500,7 @@ impl<'a> PathSource<'a> {
Res::Def(
DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn)
| DefKind::Const
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Fn
| DefKind::AssocFn
| DefKind::AssocConst
@ -3645,7 +3645,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
Some(res)
}
Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static(_), _) => {
Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static { .. }, _) => {
// This is unambiguously a fresh binding, either syntactically
// (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves
// to something unusable as a pattern (e.g., constructor function),

View File

@ -264,7 +264,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
use rustc_hir::def::DefKind;
match tcx.def_kind(def_id) {
DefKind::Fn => ForeignItemKind::Fn(tables.fn_def(def_id)),
DefKind::Static(..) => ForeignItemKind::Static(tables.static_def(def_id)),
DefKind::Static { .. } => ForeignItemKind::Static(tables.static_def(def_id)),
DefKind::ForeignTy => ForeignItemKind::Type(
tables.intern_ty(rustc_middle::ty::Ty::new_foreign(tcx, def_id)),
),

View File

@ -95,7 +95,7 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind {
DefKind::Const | DefKind::InlineConst | DefKind::AssocConst | DefKind::AnonConst => {
ItemKind::Const
}
DefKind::Static(_) => ItemKind::Static,
DefKind::Static { .. } => ItemKind::Static,
DefKind::Ctor(_, rustc_hir::def::CtorKind::Const) => ItemKind::Ctor(CtorKind::Const),
DefKind::Ctor(_, rustc_hir::def::CtorKind::Fn) => ItemKind::Ctor(CtorKind::Fn),
}

View File

@ -1205,6 +1205,7 @@ symbols! {
negative_bounds,
negative_impls,
neon,
nested,
never,
never_patterns,
never_type,

View File

@ -134,7 +134,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
| DefKind::TyParam
| DefKind::Const
| DefKind::ConstParam
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Ctor(_, _)
| DefKind::Macro(_)
| DefKind::ExternCrate

View File

@ -318,7 +318,7 @@ fn opaque_types_defined_by<'tcx>(
match kind {
DefKind::AssocFn
| DefKind::Fn
| DefKind::Static(_)
| DefKind::Static { .. }
| DefKind::Const
| DefKind::AssocConst
| DefKind::AnonConst => {

View File

@ -41,9 +41,9 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>(
}
}
// Walk over the type behind the alias
DefKind::TyAlias {..} | DefKind::AssocTy |
DefKind::TyAlias { .. } | DefKind::AssocTy |
// Walk over the type of the item
DefKind::Static(_) | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => {
DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => {
if let Some(ty) = tcx.hir_node_by_def_id(item).ty() {
// If the type of the item uses `_`, we're gonna error out anyway, but
// typeck (which type_of invokes below), will call back into opaque_types_defined_by

View File

@ -120,7 +120,7 @@ pub(crate) fn try_inline(
record_extern_fqn(cx, did, ItemType::Module);
clean::ModuleItem(build_module(cx, did, visited))
}
Res::Def(DefKind::Static(_), did) => {
Res::Def(DefKind::Static { .. }, did) => {
record_extern_fqn(cx, did, ItemType::Static);
cx.with_param_env(did, |cx| {
clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))

View File

@ -526,7 +526,7 @@ pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
| Mod
| ForeignTy
| Const
| Static(_)
| Static { .. }
| Macro(..)
| TraitAlias),
did,

View File

@ -128,7 +128,7 @@ impl ItemType {
DefKind::Fn => Self::Function,
DefKind::Mod => Self::Module,
DefKind::Const => Self::Constant,
DefKind::Static(_) => Self::Static,
DefKind::Static { .. } => Self::Static,
DefKind::Struct => Self::Struct,
DefKind::Union => Self::Union,
DefKind::Trait => Self::Trait,

View File

@ -123,7 +123,7 @@ impl Res {
DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
"const"
}
DefKind::Static(_) => "static",
DefKind::Static { .. } => "static",
// Now handle things that don't have a specific disambiguator
_ => match kind
.ns()
@ -1514,7 +1514,7 @@ impl Disambiguator {
"union" => Kind(DefKind::Union),
"module" | "mod" => Kind(DefKind::Mod),
"const" | "constant" => Kind(DefKind::Const),
"static" => Kind(DefKind::Static(Mutability::Not)),
"static" => Kind(DefKind::Static { mutability: Mutability::Not, nested: false }),
"function" | "fn" | "method" => Kind(DefKind::Fn),
"derive" => Kind(DefKind::Macro(MacroKind::Derive)),
"type" => NS(Namespace::TypeNS),
@ -1926,7 +1926,7 @@ fn resolution_failure(
| OpaqueTy
| TraitAlias
| TyParam
| Static(_) => "associated item",
| Static { .. } => "associated item",
Impl { .. } | GlobalAsm => unreachable!("not a path"),
}
} else {

View File

@ -273,7 +273,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
}
return false; // no need to walk further *on the variable*
},
Res::Def(DefKind::Static(_) | DefKind::Const, ..) => {
Res::Def(DefKind::Static{..} | DefKind::Const, ..) => {
if index_used_directly {
self.indexed_directly.insert(
seqvar.segments[0].ident.name,

View File

@ -101,7 +101,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
Res::Local(hir_id) => {
self.ids.insert(hir_id);
},
Res::Def(DefKind::Static(_), def_id) => {
Res::Def(DefKind::Static{..}, def_id) => {
let mutable = self.cx.tcx.is_mutable_static(def_id);
self.def_ids.insert(def_id, mutable);
},

View File

@ -91,7 +91,7 @@ pub(super) fn check<'tcx>(
},
hir::ExprKind::Path(ref p) => matches!(
cx.qpath_res(p, arg.hir_id),
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static(_), _)
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static{..}, _)
),
_ => false,
}

View File

@ -109,7 +109,7 @@ fn collect_unsafe_exprs<'tcx>(
ExprKind::Path(QPath::Resolved(
_,
hir::Path {
res: Res::Def(DefKind::Static(Mutability::Mut), _),
res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _),
..
},
)) => {
@ -149,7 +149,7 @@ fn collect_unsafe_exprs<'tcx>(
ExprKind::Path(QPath::Resolved(
_,
hir::Path {
res: Res::Def(DefKind::Static(Mutability::Mut), _),
res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _),
..
}
))

View File

@ -0,0 +1,23 @@
// This is a support file for ../const-mut-refs-crate.rs
// This is to test that static inners from an external
// crate like this one, still preserves the alloc.
// That is, the address from the standpoint of rustc+llvm
// is the same.
// The need for this test originated from the GH issue
// https://github.com/rust-lang/rust/issues/57349
// See also ../const-mut-refs-crate.rs for more details
// about this test.
#![feature(const_mut_refs)]
// if we used immutable references here, then promotion would
// turn the `&42` into a promoted, which gets duplicated arbitrarily.
pub static mut FOO: &'static mut i32 = &mut 42;
pub static mut BAR: &'static mut i32 = unsafe { FOO };
pub mod inner {
pub static INNER_MOD_FOO: &'static i32 = &43;
pub static INNER_MOD_BAR: &'static i32 = INNER_MOD_FOO;
}

View File

@ -0,0 +1,37 @@
//@ run-pass
//@ aux-build:const_mut_refs_crate.rs
#![feature(const_mut_refs)]
//! Regression test for https://github.com/rust-lang/rust/issues/79738
//! 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;
use other::{
inner::{INNER_MOD_BAR, INNER_MOD_FOO},
BAR, FOO,
};
pub static LOCAL_FOO: &'static i32 = &41;
pub static LOCAL_BAR: &'static i32 = LOCAL_FOO;
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_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);
assert_eq!(FOO as *const i32, COPY_OF_REMOTE_FOO as *const i32);
}
}

View 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() {}