Stop using the const_eval
query for initializers of statics
As a side effect, we now represent most promoteds as `ConstValue::Scalar` again. This is useful because all implict promoteds are just references anyway and most explicit promoteds are numeric arguments to `asm!` or SIMD instructions.
This commit is contained in:
parent
083f1d7a37
commit
2d7ac728e4
17 changed files with 53 additions and 108 deletions
|
@ -12,7 +12,7 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_hir::Node;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
use rustc_middle::mir::interpret::{
|
||||
read_target_uint, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer,
|
||||
read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer,
|
||||
};
|
||||
use rustc_middle::mir::mono::MonoItem;
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
|
@ -85,10 +85,7 @@ pub fn codegen_static_initializer(
|
|||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
def_id: DefId,
|
||||
) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> {
|
||||
let alloc = match cx.tcx.const_eval_poly(def_id)? {
|
||||
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
|
||||
val => bug!("static const eval returned {:#?}", val),
|
||||
};
|
||||
let alloc = cx.tcx.eval_static_initializer(def_id)?;
|
||||
Ok((const_alloc_to_llvm(cx, alloc), alloc))
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_ast as ast;
|
|||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar};
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
@ -867,24 +867,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let ty = constant.literal.ty;
|
||||
let size = bx.layout_of(ty).size;
|
||||
let scalar = match const_value {
|
||||
// Promoted constants are evaluated into a ByRef instead of a Scalar,
|
||||
// but we want the scalar value here.
|
||||
ConstValue::ByRef { alloc, offset } => {
|
||||
let ptr = Pointer::new(AllocId(0), offset);
|
||||
alloc
|
||||
.read_scalar(&bx, ptr, size)
|
||||
.and_then(|s| s.check_init())
|
||||
.unwrap_or_else(|e| {
|
||||
bx.tcx().sess.span_err(
|
||||
span,
|
||||
&format!("Could not evaluate asm const: {}", e),
|
||||
);
|
||||
|
||||
// We are erroring out, just emit a dummy constant.
|
||||
Scalar::from_u64(0)
|
||||
})
|
||||
}
|
||||
_ => span_bug!(span, "expected ByRef for promoted asm const"),
|
||||
ConstValue::Scalar(s) => s,
|
||||
_ => span_bug!(
|
||||
span,
|
||||
"expected Scalar for promoted asm const, but got {:#?}",
|
||||
const_value
|
||||
),
|
||||
};
|
||||
let value = scalar.assert_bits(size);
|
||||
let string = match ty.kind() {
|
||||
|
|
|
@ -1473,21 +1473,18 @@ declare_lint_pass!(
|
|||
UnusedBrokenConst => []
|
||||
);
|
||||
|
||||
fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) {
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
|
||||
// trigger the query once for all constants since that will already report the errors
|
||||
// FIXME: Use ensure here
|
||||
let _ = cx.tcx.const_eval_poly(def_id);
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
||||
match it.kind {
|
||||
hir::ItemKind::Const(_, body_id) => {
|
||||
check_const(cx, body_id);
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
|
||||
// trigger the query once for all constants since that will already report the errors
|
||||
// FIXME: Use ensure here
|
||||
let _ = cx.tcx.const_eval_poly(def_id);
|
||||
}
|
||||
hir::ItemKind::Static(_, _, body_id) => {
|
||||
check_const(cx, body_id);
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
|
||||
let _ = cx.tcx.eval_static_initializer(def_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ pub(super) fn op_to_const<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_and_turn_into_const<'tcx>(
|
||||
fn turn_into_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
constant: RawConst<'tcx>,
|
||||
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
|
||||
|
@ -191,30 +191,21 @@ fn validate_and_turn_into_const<'tcx>(
|
|||
let def_id = cid.instance.def.def_id();
|
||||
let is_static = tcx.is_static(def_id);
|
||||
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
|
||||
let val = (|| {
|
||||
let mplace = ecx.raw_const_to_mplace(constant)?;
|
||||
// Turn this into a proper constant.
|
||||
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
|
||||
// whether they become immediates.
|
||||
if is_static || cid.promoted.is_some() {
|
||||
let ptr = mplace.ptr.assert_ptr();
|
||||
Ok(ConstValue::ByRef {
|
||||
alloc: ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(),
|
||||
offset: ptr.offset,
|
||||
})
|
||||
} else {
|
||||
Ok(op_to_const(&ecx, mplace.into()))
|
||||
}
|
||||
})();
|
||||
|
||||
// FIXME: Can this ever be an error and not be a compiler bug or can we just ICE here?
|
||||
val.map_err(|error| {
|
||||
let mplace = ecx.raw_const_to_mplace(constant).map_err(|error| {
|
||||
// FIXME: Can the above ever error and not be a compiler bug or can we just ICE here?
|
||||
let err = ConstEvalErr::new(&ecx, error, None);
|
||||
err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
|
||||
diag.note(note_on_undefined_behavior_error());
|
||||
diag.emit();
|
||||
})
|
||||
})
|
||||
})?;
|
||||
assert!(
|
||||
!is_static || cid.promoted.is_some(),
|
||||
"the const eval query should not be used for statics, use `const_eval_raw` instead"
|
||||
);
|
||||
// Turn this into a proper constant.
|
||||
Ok(op_to_const(&ecx, mplace.into()))
|
||||
}
|
||||
|
||||
pub fn const_eval_validated_provider<'tcx>(
|
||||
|
@ -248,7 +239,7 @@ pub fn const_eval_validated_provider<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
tcx.const_eval_raw(key).and_then(|val| validate_and_turn_into_const(tcx, val, key))
|
||||
tcx.const_eval_raw(key).and_then(|val| turn_into_const(tcx, val, key))
|
||||
}
|
||||
|
||||
pub fn const_eval_raw_provider<'tcx>(
|
||||
|
|
|
@ -914,13 +914,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
} else {
|
||||
self.param_env
|
||||
};
|
||||
// We use `const_eval_raw` here, and get an unvalidated result. That is okay:
|
||||
// Our result will later be validated anyway, and there seems no good reason
|
||||
// to have to fail early here. This is also more consistent with
|
||||
// `Memory::get_static_alloc` which has to use `const_eval_raw` to avoid cycles.
|
||||
// FIXME: We can hit delay_span_bug if this is an invalid const, interning finds
|
||||
// that problem, but we never run validation to show an error. Can we ensure
|
||||
// this does not happen?
|
||||
let val = self.tcx.const_eval_raw(param_env.and(gid))?;
|
||||
self.raw_const_to_mplace(val)
|
||||
}
|
||||
|
|
|
@ -554,11 +554,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
ty::ConstKind::Unevaluated(def, substs, promoted) => {
|
||||
let instance = self.resolve(def.did, substs)?;
|
||||
// We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation.
|
||||
// The reason we use `const_eval_raw` everywhere else is to prevent cycles during
|
||||
// validation, because validation automatically reads through any references, thus
|
||||
// potentially requiring the current static to be evaluated again. This is not a
|
||||
// problem here, because we are building an operand which means an actual read is
|
||||
// happening.
|
||||
// The reason we use `const_eval` here is that there can never be a `ty::ConstKind`
|
||||
// that directly mentions the initializer of a static. Statics are always encoded
|
||||
// as constants with vaule `&STATIC`.
|
||||
return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?);
|
||||
}
|
||||
ty::ConstKind::Infer(..)
|
||||
|
|
|
@ -364,8 +364,10 @@ fn collect_items_rec<'tcx>(
|
|||
|
||||
recursion_depth_reset = None;
|
||||
|
||||
if let Ok(val) = tcx.const_eval_poly(def_id) {
|
||||
collect_const_value(tcx, val, &mut neighbors);
|
||||
if let Ok(alloc) = tcx.eval_static_initializer(def_id) {
|
||||
for &((), id) in alloc.relocations().values() {
|
||||
collect_miri(tcx, id, &mut neighbors);
|
||||
}
|
||||
}
|
||||
}
|
||||
MonoItem::Fn(instance) => {
|
||||
|
|
|
@ -631,14 +631,11 @@ pub fn write_allocations<'tcx>(
|
|||
None => write!(w, " (deallocated)")?,
|
||||
Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?,
|
||||
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
|
||||
match tcx.const_eval_poly(did) {
|
||||
Ok(ConstValue::ByRef { alloc, .. }) => {
|
||||
match tcx.eval_static_initializer(did) {
|
||||
Ok(alloc) => {
|
||||
write!(w, " (static: {}, ", tcx.def_path_str(did))?;
|
||||
write_allocation_track_relocs(w, alloc)?;
|
||||
}
|
||||
Ok(_) => {
|
||||
span_bug!(tcx.def_span(did), " static item without `ByRef` initializer")
|
||||
}
|
||||
Err(_) => write!(
|
||||
w,
|
||||
" (static: {}, error during initializer evaluation)",
|
||||
|
|
|
@ -111,7 +111,6 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
|
|||
use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TyCtxtInferExt};
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
|
@ -2070,8 +2069,8 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S
|
|||
// `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
|
||||
// the consumer's responsibility to ensure all bytes that have been read
|
||||
// have defined values.
|
||||
match tcx.const_eval_poly(id.to_def_id()) {
|
||||
Ok(ConstValue::ByRef { alloc, .. }) => {
|
||||
match tcx.eval_static_initializer(id.to_def_id()) {
|
||||
Ok(alloc) => {
|
||||
if alloc.relocations().len() != 0 {
|
||||
let msg = "statics with a custom `#[link_section]` must be a \
|
||||
simple list of bytes on the wasm target with no \
|
||||
|
@ -2079,7 +2078,6 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S
|
|||
tcx.sess.span_err(span, msg);
|
||||
}
|
||||
}
|
||||
Ok(_) => bug!("Matching on non-ByRef static"),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue