mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Do not allocate a second "background" alloc id for the main allocation of a static.
Instead we re-use the static's alloc id within the interpreter for its initializer to refer to the `Allocation` that only exists within the interpreter.
This commit is contained in:
parent
e2386270df
commit
73b38c661d
@ -313,6 +313,8 @@ const_eval_realloc_or_alloc_with_offset =
|
|||||||
*[other] {""}
|
*[other] {""}
|
||||||
} {$ptr} which does not point to the beginning of an object
|
} {$ptr} which does not point to the beginning of an object
|
||||||
|
|
||||||
|
const_eval_recursive_static = encountered static that tried to initialize itself with itself
|
||||||
|
|
||||||
const_eval_remainder_by_zero =
|
const_eval_remainder_by_zero =
|
||||||
calculating the remainder with a divisor of zero
|
calculating the remainder with a divisor of zero
|
||||||
const_eval_remainder_overflow =
|
const_eval_remainder_overflow =
|
||||||
|
@ -19,6 +19,7 @@ use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopTy
|
|||||||
pub enum ConstEvalErrKind {
|
pub enum ConstEvalErrKind {
|
||||||
ConstAccessesMutGlobal,
|
ConstAccessesMutGlobal,
|
||||||
ModifiedGlobal,
|
ModifiedGlobal,
|
||||||
|
RecursiveStatic,
|
||||||
AssertFailure(AssertKind<ConstInt>),
|
AssertFailure(AssertKind<ConstInt>),
|
||||||
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
|
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
|
||||||
}
|
}
|
||||||
@ -31,13 +32,14 @@ impl MachineStopType for ConstEvalErrKind {
|
|||||||
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
|
ConstAccessesMutGlobal => const_eval_const_accesses_mut_global,
|
||||||
ModifiedGlobal => const_eval_modified_global,
|
ModifiedGlobal => const_eval_modified_global,
|
||||||
Panic { .. } => const_eval_panic,
|
Panic { .. } => const_eval_panic,
|
||||||
|
RecursiveStatic => const_eval_recursive_static,
|
||||||
AssertFailure(x) => x.diagnostic_message(),
|
AssertFailure(x) => x.diagnostic_message(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) {
|
fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagnosticArgName, DiagnosticArgValue)) {
|
||||||
use ConstEvalErrKind::*;
|
use ConstEvalErrKind::*;
|
||||||
match *self {
|
match *self {
|
||||||
ConstAccessesMutGlobal | ModifiedGlobal => {}
|
RecursiveStatic | ConstAccessesMutGlobal | ModifiedGlobal => {}
|
||||||
AssertFailure(kind) => kind.add_args(adder),
|
AssertFailure(kind) => kind.add_args(adder),
|
||||||
Panic { msg, line, col, file } => {
|
Panic { msg, line, col, file } => {
|
||||||
adder("msg".into(), msg.into_diagnostic_arg());
|
adder("msg".into(), msg.into_diagnostic_arg());
|
||||||
|
@ -2,7 +2,6 @@ use either::{Left, Right};
|
|||||||
|
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
|
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
|
||||||
use rustc_middle::mir::pretty::write_allocation_bytes;
|
|
||||||
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
|
||||||
use rustc_middle::traits::Reveal;
|
use rustc_middle::traits::Reveal;
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
@ -18,8 +17,9 @@ use crate::errors;
|
|||||||
use crate::errors::ConstEvalError;
|
use crate::errors::ConstEvalError;
|
||||||
use crate::interpret::eval_nullary_intrinsic;
|
use crate::interpret::eval_nullary_intrinsic;
|
||||||
use crate::interpret::{
|
use crate::interpret::{
|
||||||
intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
|
create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc, CtfeValidationMode,
|
||||||
InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup,
|
GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind,
|
||||||
|
OpTy, RefTracking, StackPopCleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns a pointer to where the result lives
|
// Returns a pointer to where the result lives
|
||||||
@ -47,7 +47,21 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
|||||||
);
|
);
|
||||||
let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
|
let layout = ecx.layout_of(body.bound_return_ty().instantiate(tcx, cid.instance.args))?;
|
||||||
assert!(layout.is_sized());
|
assert!(layout.is_sized());
|
||||||
let ret = ecx.allocate(layout, MemoryKind::Stack)?;
|
|
||||||
|
let intern_kind = if cid.promoted.is_some() {
|
||||||
|
InternKind::Promoted
|
||||||
|
} else {
|
||||||
|
match tcx.static_mutability(cid.instance.def_id()) {
|
||||||
|
Some(m) => InternKind::Static(m),
|
||||||
|
None => InternKind::Constant,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = if let InternKind::Static(_) = intern_kind {
|
||||||
|
create_static_alloc(ecx, cid.instance.def_id(), layout)?
|
||||||
|
} else {
|
||||||
|
ecx.allocate(layout, MemoryKind::Stack)?
|
||||||
|
};
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"eval_body_using_ecx: pushing stack frame for global: {}{}",
|
"eval_body_using_ecx: pushing stack frame for global: {}{}",
|
||||||
@ -67,14 +81,6 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
|||||||
while ecx.step()? {}
|
while ecx.step()? {}
|
||||||
|
|
||||||
// Intern the result
|
// Intern the result
|
||||||
let intern_kind = if cid.promoted.is_some() {
|
|
||||||
InternKind::Promoted
|
|
||||||
} else {
|
|
||||||
match tcx.static_mutability(cid.instance.def_id()) {
|
|
||||||
Some(m) => InternKind::Static(m),
|
|
||||||
None => InternKind::Constant,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
@ -259,7 +265,7 @@ pub fn eval_static_initializer_provider<'tcx>(
|
|||||||
|
|
||||||
let instance = ty::Instance::mono(tcx, def_id.to_def_id());
|
let instance = ty::Instance::mono(tcx, def_id.to_def_id());
|
||||||
let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
|
let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
|
||||||
let ecx = InterpCx::new(
|
let mut ecx = InterpCx::new(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.def_span(def_id),
|
tcx.def_span(def_id),
|
||||||
ty::ParamEnv::reveal_all(),
|
ty::ParamEnv::reveal_all(),
|
||||||
@ -267,8 +273,9 @@ pub fn eval_static_initializer_provider<'tcx>(
|
|||||||
// they do not have to behave "as if" they were evaluated at runtime.
|
// they do not have to behave "as if" they were evaluated at runtime.
|
||||||
CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error),
|
CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error),
|
||||||
);
|
);
|
||||||
let alloc_id = eval_in_interpreter(ecx, cid, true)?.alloc_id;
|
let alloc_id = eval_in_interpreter(&mut ecx, cid, true)?.alloc_id;
|
||||||
let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
|
let alloc = take_static_root_alloc(&mut ecx, alloc_id);
|
||||||
|
let alloc = tcx.mk_const_alloc(alloc);
|
||||||
Ok(alloc)
|
Ok(alloc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +306,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||||||
let def = cid.instance.def.def_id();
|
let def = cid.instance.def.def_id();
|
||||||
let is_static = tcx.is_static(def);
|
let is_static = tcx.is_static(def);
|
||||||
|
|
||||||
let ecx = InterpCx::new(
|
let mut ecx = InterpCx::new(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.def_span(def),
|
tcx.def_span(def),
|
||||||
key.param_env,
|
key.param_env,
|
||||||
@ -309,11 +316,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||||||
// so we have to reject reading mutable global memory.
|
// so we have to reject reading mutable global memory.
|
||||||
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
|
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
|
||||||
);
|
);
|
||||||
eval_in_interpreter(ecx, cid, is_static)
|
eval_in_interpreter(&mut ecx, cid, is_static)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_in_interpreter<'mir, 'tcx>(
|
pub fn eval_in_interpreter<'mir, 'tcx>(
|
||||||
mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
|
||||||
cid: GlobalId<'tcx>,
|
cid: GlobalId<'tcx>,
|
||||||
is_static: bool,
|
is_static: bool,
|
||||||
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
|
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
|
||||||
@ -321,7 +328,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
|
|||||||
debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());
|
debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());
|
||||||
|
|
||||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||||
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) {
|
match res.and_then(|body| eval_body_using_ecx(ecx, cid, body)) {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let (error, backtrace) = error.into_parts();
|
let (error, backtrace) = error.into_parts();
|
||||||
backtrace.print_backtrace();
|
backtrace.print_backtrace();
|
||||||
@ -356,8 +363,11 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
|
|||||||
}
|
}
|
||||||
Ok(mplace) => {
|
Ok(mplace) => {
|
||||||
// Since evaluation had no errors, validate the resulting constant.
|
// Since evaluation had no errors, validate the resulting constant.
|
||||||
// This is a separate `try` block to provide more targeted error reporting.
|
|
||||||
|
// Temporarily allow access to the static_root_alloc_id for the purpose of validation.
|
||||||
|
let static_root_alloc_id = ecx.machine.static_root_alloc_id.take();
|
||||||
let validation = const_validate_mplace(&ecx, &mplace, cid);
|
let validation = const_validate_mplace(&ecx, &mplace, cid);
|
||||||
|
ecx.machine.static_root_alloc_id = static_root_alloc_id;
|
||||||
|
|
||||||
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
|
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
|
||||||
|
|
||||||
@ -409,15 +419,9 @@ pub fn const_report_error<'mir, 'tcx>(
|
|||||||
|
|
||||||
let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
|
let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
|
||||||
|
|
||||||
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
|
||||||
let mut bytes = String::new();
|
let (size, align, _) = ecx.get_alloc_info(alloc_id);
|
||||||
if alloc.size() != abi::Size::ZERO {
|
let raw_bytes = errors::RawBytesNote { size: size.bytes(), align: align.bytes(), bytes };
|
||||||
bytes = "\n".into();
|
|
||||||
// FIXME(translation) there might be pieces that are translatable.
|
|
||||||
write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
|
|
||||||
}
|
|
||||||
let raw_bytes =
|
|
||||||
errors::RawBytesNote { size: alloc.size().bytes(), align: alloc.align.bytes(), bytes };
|
|
||||||
|
|
||||||
crate::const_eval::report(
|
crate::const_eval::report(
|
||||||
*ecx.tcx,
|
*ecx.tcx,
|
||||||
|
@ -58,6 +58,9 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
|
|||||||
|
|
||||||
/// Whether to check alignment during evaluation.
|
/// Whether to check alignment during evaluation.
|
||||||
pub(super) check_alignment: CheckAlignment,
|
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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -91,6 +94,7 @@ impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
|
|||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
can_access_mut_global,
|
can_access_mut_global,
|
||||||
check_alignment,
|
check_alignment,
|
||||||
|
static_root_alloc_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -746,6 +750,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||||||
// Everything else is fine.
|
// Everything else is fine.
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn before_alloc_read(
|
||||||
|
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||||
|
alloc_id: AllocId,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
if Some(alloc_id) == ecx.machine.static_root_alloc_id {
|
||||||
|
Err(ConstEvalErrKind::RecursiveStatic.into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups
|
// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups
|
||||||
|
@ -899,7 +899,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||||||
.local_to_op(self.frame(), mir::RETURN_PLACE, None)
|
.local_to_op(self.frame(), mir::RETURN_PLACE, None)
|
||||||
.expect("return place should always be live");
|
.expect("return place should always be live");
|
||||||
let dest = self.frame().return_place.clone();
|
let dest = self.frame().return_place.clone();
|
||||||
let err = self.copy_op_allow_transmute(&op, &dest);
|
let err = if self.stack().len() == 1 {
|
||||||
|
// The initializer of constants and statics will get validated separately
|
||||||
|
// after the constant has been fully evaluated. While we could fall back to the default
|
||||||
|
// code path, that will cause -Zenforce-validity to cycle on static initializers.
|
||||||
|
// Reading from a static's memory is not allowed during its evaluation, and will always
|
||||||
|
// trigger a cycle error. Validation must read from the memory of the current item.
|
||||||
|
// For Miri this means we do not validate the root frame return value,
|
||||||
|
// but Miri anyway calls `read_target_isize` on that so separate validation
|
||||||
|
// is not needed.
|
||||||
|
self.copy_op_no_dest_validation(&op, &dest)
|
||||||
|
} else {
|
||||||
|
self.copy_op_allow_transmute(&op, &dest)
|
||||||
|
};
|
||||||
trace!("return value: {:?}", self.dump_place(&dest));
|
trace!("return value: {:?}", self.dump_place(&dest));
|
||||||
// We delay actually short-circuiting on this error until *after* the stack frame is
|
// We delay actually short-circuiting on this error until *after* the stack frame is
|
||||||
// popped, since we want this error to be attributed to the caller, whose type defines
|
// popped, since we want this error to be attributed to the caller, whose type defines
|
||||||
|
@ -85,6 +85,8 @@ pub enum InternKind {
|
|||||||
///
|
///
|
||||||
/// This *cannot raise an interpreter error*. Doing so is left to validation, which
|
/// This *cannot raise an interpreter error*. Doing so is left to validation, which
|
||||||
/// tracks where in the value we are and thus can show much better error messages.
|
/// tracks where in the value we are and thus can show much better error messages.
|
||||||
|
///
|
||||||
|
/// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller.
|
||||||
#[instrument(level = "debug", skip(ecx))]
|
#[instrument(level = "debug", skip(ecx))]
|
||||||
pub fn intern_const_alloc_recursive<
|
pub fn intern_const_alloc_recursive<
|
||||||
'mir,
|
'mir,
|
||||||
@ -97,12 +99,12 @@ pub fn intern_const_alloc_recursive<
|
|||||||
) -> Result<(), ErrorGuaranteed> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
// We are interning recursively, and for mutability we are distinguishing the "root" allocation
|
// We are interning recursively, and for mutability we are distinguishing the "root" allocation
|
||||||
// that we are starting in, and all other allocations that we are encountering recursively.
|
// that we are starting in, and all other allocations that we are encountering recursively.
|
||||||
let (base_mutability, inner_mutability) = match intern_kind {
|
let (base_mutability, inner_mutability, is_static) = match intern_kind {
|
||||||
InternKind::Constant | InternKind::Promoted => {
|
InternKind::Constant | InternKind::Promoted => {
|
||||||
// Completely immutable. Interning anything mutably here can only lead to unsoundness,
|
// Completely immutable. Interning anything mutably here can only lead to unsoundness,
|
||||||
// since all consts are conceptually independent values but share the same underlying
|
// since all consts are conceptually independent values but share the same underlying
|
||||||
// memory.
|
// memory.
|
||||||
(Mutability::Not, Mutability::Not)
|
(Mutability::Not, Mutability::Not, false)
|
||||||
}
|
}
|
||||||
InternKind::Static(Mutability::Not) => {
|
InternKind::Static(Mutability::Not) => {
|
||||||
(
|
(
|
||||||
@ -115,22 +117,31 @@ pub fn intern_const_alloc_recursive<
|
|||||||
// Inner allocations are never mutable. They can only arise via the "tail
|
// Inner allocations are never mutable. They can only arise via the "tail
|
||||||
// expression" / "outer scope" rule, and we treat them consistently with `const`.
|
// expression" / "outer scope" rule, and we treat them consistently with `const`.
|
||||||
Mutability::Not,
|
Mutability::Not,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
InternKind::Static(Mutability::Mut) => {
|
InternKind::Static(Mutability::Mut) => {
|
||||||
// Just make everything mutable. We accept code like
|
// Just make everything mutable. We accept code like
|
||||||
// `static mut X = &mut [42]`, so even inner allocations need to be mutable.
|
// `static mut X = &mut [42]`, so even inner allocations need to be mutable.
|
||||||
(Mutability::Mut, Mutability::Mut)
|
(Mutability::Mut, Mutability::Mut, true)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Intern the base allocation, and initialize todo list for recursive interning.
|
// Intern the base allocation, and initialize todo list for recursive interning.
|
||||||
let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
|
let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id();
|
||||||
|
trace!(?base_alloc_id, ?base_mutability);
|
||||||
// First we intern the base allocation, as it requires a different mutability.
|
// First we intern the base allocation, as it requires a different mutability.
|
||||||
// This gives us the initial set of nested allocations, which will then all be processed
|
// This gives us the initial set of nested allocations, which will then all be processed
|
||||||
// recursively in the loop below.
|
// recursively in the loop below.
|
||||||
let mut todo: Vec<_> =
|
let mut todo: Vec<_> = if is_static {
|
||||||
intern_shallow(ecx, base_alloc_id, base_mutability).unwrap().map(|prov| prov).collect();
|
// Do not steal the root allocation, we need it later for `take_static_root_alloc`
|
||||||
|
// But still change its mutability to match the requested one.
|
||||||
|
let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap();
|
||||||
|
alloc.1.mutability = base_mutability;
|
||||||
|
alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect()
|
||||||
|
} else {
|
||||||
|
intern_shallow(ecx, base_alloc_id, base_mutability).unwrap().map(|prov| prov).collect()
|
||||||
|
};
|
||||||
// We need to distinguish "has just been interned" from "was already in `tcx`",
|
// We need to distinguish "has just been interned" from "was already in `tcx`",
|
||||||
// so we track this in a separate set.
|
// so we track this in a separate set.
|
||||||
let mut just_interned: FxHashSet<_> = std::iter::once(base_alloc_id).collect();
|
let mut just_interned: FxHashSet<_> = std::iter::once(base_alloc_id).collect();
|
||||||
@ -148,7 +159,17 @@ pub fn intern_const_alloc_recursive<
|
|||||||
// before validation, and interning doesn't know the type of anything, this means we can't show
|
// before validation, and interning doesn't know the type of anything, this means we can't show
|
||||||
// better errors. Maybe we should consider doing validation before interning in the future.
|
// better errors. Maybe we should consider doing validation before interning in the future.
|
||||||
while let Some(prov) = todo.pop() {
|
while let Some(prov) = todo.pop() {
|
||||||
|
trace!(?prov);
|
||||||
let alloc_id = prov.alloc_id();
|
let alloc_id = prov.alloc_id();
|
||||||
|
|
||||||
|
if base_alloc_id == alloc_id && is_static {
|
||||||
|
// This is a pointer to the static itself. It's ok for a static to refer to itself,
|
||||||
|
// even mutably. Whether that mutable pointer is legal at all is checked in validation.
|
||||||
|
// See tests/ui/statics/recursive_interior_mut.rs for how such a situation can occur.
|
||||||
|
// We also already collected all the nested allocations, so there's no need to do that again.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Crucially, we check this *before* checking whether the `alloc_id`
|
// Crucially, we check this *before* checking whether the `alloc_id`
|
||||||
// has already been interned. The point of this check is to ensure that when
|
// has already been interned. The point of this check is to ensure that when
|
||||||
// there are multiple pointers to the same allocation, they are *all* immutable.
|
// there are multiple pointers to the same allocation, they are *all* immutable.
|
||||||
@ -176,6 +197,7 @@ pub fn intern_const_alloc_recursive<
|
|||||||
// `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
|
// `&None::<Cell<i32>>` lead to promotion that can produce mutable pointers. We rely
|
||||||
// on the promotion analysis not screwing up to ensure that it is sound to intern
|
// on the promotion analysis not screwing up to ensure that it is sound to intern
|
||||||
// promoteds as immutable.
|
// promoteds as immutable.
|
||||||
|
trace!("found bad mutable pointer");
|
||||||
found_bad_mutable_pointer = true;
|
found_bad_mutable_pointer = true;
|
||||||
}
|
}
|
||||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||||
|
@ -388,6 +388,8 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||||||
/// Takes read-only access to the allocation so we can keep all the memory read
|
/// Takes read-only access to the allocation so we can keep all the memory read
|
||||||
/// operations take `&self`. Use a `RefCell` in `AllocExtra` if you
|
/// operations take `&self`. Use a `RefCell` in `AllocExtra` if you
|
||||||
/// need to mutate.
|
/// need to mutate.
|
||||||
|
///
|
||||||
|
/// This is not invoked for ZST accesses, as no read actually happens.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn before_memory_read(
|
fn before_memory_read(
|
||||||
_tcx: TyCtxtAt<'tcx>,
|
_tcx: TyCtxtAt<'tcx>,
|
||||||
@ -399,7 +401,20 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hook for performing extra checks on any memory read access,
|
||||||
|
/// that involves an allocation, even ZST reads.
|
||||||
|
///
|
||||||
|
/// Used to prevent statics from self-initializing by reading from their own memory
|
||||||
|
/// as it is being initialized.
|
||||||
|
fn before_alloc_read(
|
||||||
|
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||||
|
_alloc_id: AllocId,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Hook for performing extra checks on a memory write access.
|
/// Hook for performing extra checks on a memory write access.
|
||||||
|
/// This is not invoked for ZST accesses, as no write actually happens.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn before_memory_write(
|
fn before_memory_write(
|
||||||
_tcx: TyCtxtAt<'tcx>,
|
_tcx: TyCtxtAt<'tcx>,
|
||||||
|
@ -624,19 +624,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||||||
size,
|
size,
|
||||||
CheckInAllocMsg::MemoryAccessTest,
|
CheckInAllocMsg::MemoryAccessTest,
|
||||||
|alloc_id, offset, prov| {
|
|alloc_id, offset, prov| {
|
||||||
|
// We want to call the hook on *all* accesses that involve an AllocId,
|
||||||
|
// including zero-sized accesses. That means we have to do it here
|
||||||
|
// rather than below in the `Some` branch.
|
||||||
|
M::before_alloc_read(self, alloc_id)?;
|
||||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||||
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
|
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
|
if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
|
||||||
let range = alloc_range(offset, size);
|
let range = alloc_range(offset, size);
|
||||||
M::before_memory_read(self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
|
M::before_memory_read(self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?;
|
||||||
Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
|
Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
|
||||||
} else {
|
} else {
|
||||||
// Even in this branch we have to be sure that we actually access the allocation, in
|
|
||||||
// order to ensure that `static FOO: Type = FOO;` causes a cycle error instead of
|
|
||||||
// magically pulling *any* ZST value from the ether. However, the `get_raw` above is
|
|
||||||
// always called when `ptr` has an `AllocId`.
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -855,6 +856,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||||||
DumpAllocs { ecx: self, allocs }
|
DumpAllocs { ecx: self, allocs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print the allocation's bytes, without any nested allocations.
|
||||||
|
pub fn print_alloc_bytes_for_diagnostics(&self, id: AllocId) -> String {
|
||||||
|
// Using the "raw" access to avoid the `before_alloc_read` hook, we specifically
|
||||||
|
// want to be able to read all memory for diagnostics, even if that is cyclic.
|
||||||
|
let alloc = self.get_alloc_raw(id).unwrap();
|
||||||
|
let mut bytes = String::new();
|
||||||
|
if alloc.size() != Size::ZERO {
|
||||||
|
bytes = "\n".into();
|
||||||
|
// FIXME(translation) there might be pieces that are translatable.
|
||||||
|
rustc_middle::mir::pretty::write_allocation_bytes(*self.tcx, alloc, &mut bytes, " ")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
/// Find leaked allocations. Allocations reachable from `static_roots` or a `Global` allocation
|
/// Find leaked allocations. Allocations reachable from `static_roots` or a `Global` allocation
|
||||||
/// are not considered leaked, as well as leaks whose kind's `may_leak()` returns true.
|
/// are not considered leaked, as well as leaks whose kind's `may_leak()` returns true.
|
||||||
pub fn find_leaked_allocations(
|
pub fn find_leaked_allocations(
|
||||||
|
@ -39,4 +39,5 @@ use self::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::intrinsics::eval_nullary_intrinsic;
|
pub(crate) use self::intrinsics::eval_nullary_intrinsic;
|
||||||
|
pub(crate) use self::util::{create_static_alloc, take_static_root_alloc};
|
||||||
use eval_context::{from_known_layout, mir_assign_valid_types};
|
use eval_context::{from_known_layout, mir_assign_valid_types};
|
||||||
|
@ -758,6 +758,22 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copies the data from an operand to a place.
|
||||||
|
/// The layouts of the `src` and `dest` may disagree.
|
||||||
|
/// Does not perform validation of the destination.
|
||||||
|
/// The only known use case for this function is checking the return
|
||||||
|
/// value of a static during stack frame popping.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(super) fn copy_op_no_dest_validation(
|
||||||
|
&mut self,
|
||||||
|
src: &impl Readable<'tcx, M::Provenance>,
|
||||||
|
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||||
|
) -> InterpResult<'tcx> {
|
||||||
|
self.copy_op_inner(
|
||||||
|
src, dest, /* allow_transmute */ true, /* validate_dest */ false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Copies the data from an operand to a place.
|
/// Copies the data from an operand to a place.
|
||||||
/// The layouts of the `src` and `dest` may disagree.
|
/// The layouts of the `src` and `dest` may disagree.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -766,17 +782,22 @@ where
|
|||||||
src: &impl Readable<'tcx, M::Provenance>,
|
src: &impl Readable<'tcx, M::Provenance>,
|
||||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
self.copy_op_inner(src, dest, /* allow_transmute */ true)
|
self.copy_op_inner(
|
||||||
|
src, dest, /* allow_transmute */ true, /* validate_dest */ true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies the data from an operand to a place.
|
/// Copies the data from an operand to a place.
|
||||||
|
/// `src` and `dest` must have the same layout and the copied value will be validated.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn copy_op(
|
pub fn copy_op(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: &impl Readable<'tcx, M::Provenance>,
|
src: &impl Readable<'tcx, M::Provenance>,
|
||||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
self.copy_op_inner(src, dest, /* allow_transmute */ false)
|
self.copy_op_inner(
|
||||||
|
src, dest, /* allow_transmute */ false, /* validate_dest */ true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies the data from an operand to a place.
|
/// Copies the data from an operand to a place.
|
||||||
@ -788,6 +809,7 @@ where
|
|||||||
src: &impl Readable<'tcx, M::Provenance>,
|
src: &impl Readable<'tcx, M::Provenance>,
|
||||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||||
allow_transmute: bool,
|
allow_transmute: bool,
|
||||||
|
validate_dest: bool,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
// Generally for transmutation, data must be valid both at the old and new type.
|
// Generally for transmutation, data must be valid both at the old and new type.
|
||||||
// But if the types are the same, the 2nd validation below suffices.
|
// But if the types are the same, the 2nd validation below suffices.
|
||||||
@ -798,7 +820,7 @@ where
|
|||||||
// Do the actual copy.
|
// Do the actual copy.
|
||||||
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
||||||
|
|
||||||
if M::enforce_validity(self, dest.layout()) {
|
if validate_dest && M::enforce_validity(self, dest.layout()) {
|
||||||
// Data got changed, better make sure it matches the type!
|
// Data got changed, better make sure it matches the type!
|
||||||
self.validate_operand(&dest.to_op(self)?)?;
|
self.validate_operand(&dest.to_op(self)?)?;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
use rustc_middle::mir::interpret::InterpResult;
|
use crate::const_eval::CompileTimeEvalContext;
|
||||||
|
use crate::interpret::{MemPlaceMeta, MemoryKind};
|
||||||
|
use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer};
|
||||||
|
use rustc_middle::ty::layout::TyAndLayout;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||||
};
|
};
|
||||||
|
use rustc_span::def_id::DefId;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use super::MPlaceTy;
|
||||||
|
|
||||||
/// Checks whether a type contains generic parameters which must be instantiated.
|
/// Checks whether a type contains generic parameters which must be instantiated.
|
||||||
///
|
///
|
||||||
/// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization
|
/// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization
|
||||||
@ -73,3 +79,23 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>(
|
||||||
|
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||||
|
alloc_id: AllocId,
|
||||||
|
) -> Allocation {
|
||||||
|
ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>(
|
||||||
|
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
|
||||||
|
static_def_id: DefId,
|
||||||
|
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);
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
@ -27,9 +27,9 @@ use rustc_target::abi::{
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
format_interp_error, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx,
|
format_interp_error, machine::AllocMap, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy,
|
||||||
InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar,
|
Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable,
|
||||||
ValueVisitor,
|
Scalar, ValueVisitor,
|
||||||
};
|
};
|
||||||
|
|
||||||
// for the validation errors
|
// for the validation errors
|
||||||
@ -712,11 +712,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||||||
fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool {
|
fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool {
|
||||||
if let Some(mplace) = op.as_mplace_or_imm().left() {
|
if let Some(mplace) = op.as_mplace_or_imm().left() {
|
||||||
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
|
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
|
||||||
if self.ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().mutability
|
let mutability = match self.ecx.tcx.global_alloc(alloc_id) {
|
||||||
== Mutability::Mut
|
GlobalAlloc::Static(_) => {
|
||||||
{
|
self.ecx.memory.alloc_map.get(alloc_id).unwrap().1.mutability
|
||||||
return true;
|
}
|
||||||
}
|
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
|
||||||
|
_ => span_bug!(self.ecx.tcx.span, "not a memory allocation"),
|
||||||
|
};
|
||||||
|
return mutability == Mutability::Mut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
@ -1,21 +1,35 @@
|
|||||||
error[E0391]: cycle detected when evaluating initializer of static `FOO`
|
error[E0080]: could not evaluate static initializer
|
||||||
--> $DIR/recursive-zst-static.rs:10:18
|
--> $DIR/recursive-zst-static.rs:10:18
|
||||||
|
|
|
|
||||||
LL | static FOO: () = FOO;
|
LL | static FOO: () = FOO;
|
||||||
| ^^^
|
| ^^^ encountered static that tried to initialize itself with itself
|
||||||
|
|
||||||
|
error[E0391]: cycle detected when evaluating initializer of static `A`
|
||||||
|
--> $DIR/recursive-zst-static.rs:13:16
|
||||||
|
|
|
|
||||||
= note: ...which immediately requires evaluating initializer of static `FOO` again
|
LL | static A: () = B;
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: ...which requires evaluating initializer of static `B`...
|
||||||
|
--> $DIR/recursive-zst-static.rs:14:16
|
||||||
|
|
|
||||||
|
LL | static B: () = A;
|
||||||
|
| ^
|
||||||
|
= note: ...which again requires evaluating initializer of static `A`, completing the cycle
|
||||||
note: cycle used when linting top-level module
|
note: cycle used when linting top-level module
|
||||||
--> $DIR/recursive-zst-static.rs:10:1
|
--> $DIR/recursive-zst-static.rs:10:1
|
||||||
|
|
|
|
||||||
LL | / static FOO: () = FOO;
|
LL | / static FOO: () = FOO;
|
||||||
LL | |
|
LL | |
|
||||||
LL | | fn main() {
|
LL | |
|
||||||
|
LL | | static A: () = B;
|
||||||
|
... |
|
||||||
LL | | FOO
|
LL | | FOO
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0391`.
|
Some errors have detailed explanations: E0080, E0391.
|
||||||
|
For more information about an error, try `rustc --explain E0080`.
|
||||||
|
@ -7,7 +7,11 @@
|
|||||||
// can depend on this fact and will thus do unsound things when it is violated.
|
// can depend on this fact and will thus do unsound things when it is violated.
|
||||||
// See https://github.com/rust-lang/rust/issues/71078 for more details.
|
// See https://github.com/rust-lang/rust/issues/71078 for more details.
|
||||||
|
|
||||||
static FOO: () = FOO; //~ cycle detected when evaluating initializer of static `FOO`
|
static FOO: () = FOO;
|
||||||
|
//~^ ERROR could not evaluate static initializer
|
||||||
|
|
||||||
|
static A: () = B; //~ cycle detected when evaluating initializer of static `A`
|
||||||
|
static B: () = A;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
FOO
|
FOO
|
||||||
|
@ -1,21 +1,35 @@
|
|||||||
error[E0391]: cycle detected when evaluating initializer of static `FOO`
|
error[E0080]: could not evaluate static initializer
|
||||||
--> $DIR/recursive-zst-static.rs:10:18
|
--> $DIR/recursive-zst-static.rs:10:18
|
||||||
|
|
|
|
||||||
LL | static FOO: () = FOO;
|
LL | static FOO: () = FOO;
|
||||||
| ^^^
|
| ^^^ encountered static that tried to initialize itself with itself
|
||||||
|
|
||||||
|
error[E0391]: cycle detected when evaluating initializer of static `A`
|
||||||
|
--> $DIR/recursive-zst-static.rs:13:16
|
||||||
|
|
|
|
||||||
= note: ...which immediately requires evaluating initializer of static `FOO` again
|
LL | static A: () = B;
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: ...which requires evaluating initializer of static `B`...
|
||||||
|
--> $DIR/recursive-zst-static.rs:14:16
|
||||||
|
|
|
||||||
|
LL | static B: () = A;
|
||||||
|
| ^
|
||||||
|
= note: ...which again requires evaluating initializer of static `A`, completing the cycle
|
||||||
note: cycle used when linting top-level module
|
note: cycle used when linting top-level module
|
||||||
--> $DIR/recursive-zst-static.rs:10:1
|
--> $DIR/recursive-zst-static.rs:10:1
|
||||||
|
|
|
|
||||||
LL | / static FOO: () = FOO;
|
LL | / static FOO: () = FOO;
|
||||||
LL | |
|
LL | |
|
||||||
LL | | fn main() {
|
LL | |
|
||||||
|
LL | | static A: () = B;
|
||||||
|
... |
|
||||||
LL | | FOO
|
LL | | FOO
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0391`.
|
Some errors have detailed explanations: E0080, E0391.
|
||||||
|
For more information about an error, try `rustc --explain E0080`.
|
||||||
|
@ -3,8 +3,8 @@ pub static mut B: () = unsafe { A = 1; };
|
|||||||
//~^ ERROR could not evaluate static initializer
|
//~^ ERROR could not evaluate static initializer
|
||||||
|
|
||||||
pub static mut C: u32 = unsafe { C = 1; 0 };
|
pub static mut C: u32 = unsafe { C = 1; 0 };
|
||||||
//~^ ERROR cycle detected
|
|
||||||
|
|
||||||
pub static D: u32 = D;
|
pub static D: u32 = D;
|
||||||
|
//~^ ERROR could not evaluate static initializer
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -4,27 +4,12 @@ error[E0080]: could not evaluate static initializer
|
|||||||
LL | pub static mut B: () = unsafe { A = 1; };
|
LL | pub static mut B: () = unsafe { A = 1; };
|
||||||
| ^^^^^ modifying a static's initial value from another static's initializer
|
| ^^^^^ modifying a static's initial value from another static's initializer
|
||||||
|
|
||||||
error[E0391]: cycle detected when evaluating initializer of static `C`
|
error[E0080]: could not evaluate static initializer
|
||||||
--> $DIR/write-to-static-mut-in-static.rs:5:34
|
--> $DIR/write-to-static-mut-in-static.rs:7:21
|
||||||
|
|
|
|
||||||
LL | pub static mut C: u32 = unsafe { C = 1; 0 };
|
LL | pub static D: u32 = D;
|
||||||
| ^^^^^
|
| ^ encountered static that tried to initialize itself with itself
|
||||||
|
|
|
||||||
= note: ...which immediately requires evaluating initializer of static `C` again
|
|
||||||
note: cycle used when linting top-level module
|
|
||||||
--> $DIR/write-to-static-mut-in-static.rs:1:1
|
|
||||||
|
|
|
||||||
LL | / pub static mut A: u32 = 0;
|
|
||||||
LL | | pub static mut B: () = unsafe { A = 1; };
|
|
||||||
LL | |
|
|
||||||
LL | |
|
|
||||||
... |
|
|
||||||
LL | |
|
|
||||||
LL | | fn main() {}
|
|
||||||
| |____________^
|
|
||||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0080, E0391.
|
For more information about this error, try `rustc --explain E0080`.
|
||||||
For more information about an error, try `rustc --explain E0080`.
|
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
pub static FOO: u32 = FOO;
|
pub static FOO: u32 = FOO;
|
||||||
//~^ ERROR cycle detected when evaluating initializer of static `FOO`
|
//~^ ERROR could not evaluate static initializer
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union Foo {
|
||||||
|
x: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static BAR: Foo = BAR;
|
||||||
|
//~^ ERROR could not evaluate static initializer
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
error[E0391]: cycle detected when evaluating initializer of static `FOO`
|
error[E0080]: could not evaluate static initializer
|
||||||
--> $DIR/recursive-static-definition.rs:1:23
|
--> $DIR/recursive-static-definition.rs:1:23
|
||||||
|
|
|
|
||||||
LL | pub static FOO: u32 = FOO;
|
LL | pub static FOO: u32 = FOO;
|
||||||
| ^^^
|
| ^^^ encountered static that tried to initialize itself with itself
|
||||||
|
|
|
||||||
= note: ...which immediately requires evaluating initializer of static `FOO` again
|
|
||||||
note: cycle used when linting top-level module
|
|
||||||
--> $DIR/recursive-static-definition.rs:1:1
|
|
||||||
|
|
|
||||||
LL | / pub static FOO: u32 = FOO;
|
|
||||||
LL | |
|
|
||||||
LL | |
|
|
||||||
LL | | fn main() {}
|
|
||||||
| |____________^
|
|
||||||
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error[E0080]: could not evaluate static initializer
|
||||||
|
--> $DIR/recursive-static-definition.rs:9:23
|
||||||
|
|
|
||||||
|
LL | pub static BAR: Foo = BAR;
|
||||||
|
| ^^^ encountered static that tried to initialize itself with itself
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0391`.
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0080`.
|
||||||
|
Loading…
Reference in New Issue
Block a user