Auto merge of #112718 - oli-obk:SIMD-destructure_mir_const, r=cjgillot
Make simd_shuffle_indices use valtrees This removes the second-to-last user of the `destructure_mir_constant` query. So in a follow-up we can remove the query and just move the query provider function directly into pretty printing (which is the last user). cc `@rust-lang/clippy` there's a small functional change, but I think it is correct?
This commit is contained in:
commit
72b2101434
7 changed files with 106 additions and 58 deletions
|
@ -870,13 +870,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
// promotes any complex rvalues to constants.
|
// promotes any complex rvalues to constants.
|
||||||
if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") {
|
if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") {
|
||||||
if let mir::Operand::Constant(constant) = arg {
|
if let mir::Operand::Constant(constant) = arg {
|
||||||
let c = self.eval_mir_constant(constant);
|
let (llval, ty) = self.simd_shuffle_indices(&bx, constant);
|
||||||
let (llval, ty) = self.simd_shuffle_indices(
|
|
||||||
&bx,
|
|
||||||
constant.span,
|
|
||||||
self.monomorphize(constant.ty()),
|
|
||||||
c,
|
|
||||||
);
|
|
||||||
return OperandRef {
|
return OperandRef {
|
||||||
val: Immediate(llval),
|
val: Immediate(llval),
|
||||||
layout: bx.layout_of(ty),
|
layout: bx.layout_of(ty),
|
||||||
|
|
|
@ -5,7 +5,6 @@ use rustc_middle::mir;
|
||||||
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
|
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
|
||||||
use rustc_middle::ty::layout::HasTyCtxt;
|
use rustc_middle::ty::layout::HasTyCtxt;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_span::source_map::Span;
|
|
||||||
use rustc_target::abi::Abi;
|
use rustc_target::abi::Abi;
|
||||||
|
|
||||||
use super::FunctionCx;
|
use super::FunctionCx;
|
||||||
|
@ -59,22 +58,40 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
|
||||||
|
/// that the given `constant` is an `ConstantKind::Unevaluated` and must be convertible to
|
||||||
|
/// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
|
||||||
|
pub fn eval_unevaluated_mir_constant_to_valtree(
|
||||||
|
&self,
|
||||||
|
constant: &mir::Constant<'tcx>,
|
||||||
|
) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> {
|
||||||
|
let uv = match constant.literal {
|
||||||
|
mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(),
|
||||||
|
other => span_bug!(constant.span, "{other:#?}"),
|
||||||
|
};
|
||||||
|
let uv = self.monomorphize(uv);
|
||||||
|
self.cx.tcx().const_eval_resolve_for_typeck(
|
||||||
|
ty::ParamEnv::reveal_all(),
|
||||||
|
uv,
|
||||||
|
Some(constant.span),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// process constant containing SIMD shuffle indices
|
/// process constant containing SIMD shuffle indices
|
||||||
pub fn simd_shuffle_indices(
|
pub fn simd_shuffle_indices(
|
||||||
&mut self,
|
&mut self,
|
||||||
bx: &Bx,
|
bx: &Bx,
|
||||||
span: Span,
|
constant: &mir::Constant<'tcx>,
|
||||||
ty: Ty<'tcx>,
|
|
||||||
constant: Result<ConstValue<'tcx>, ErrorHandled>,
|
|
||||||
) -> (Bx::Value, Ty<'tcx>) {
|
) -> (Bx::Value, Ty<'tcx>) {
|
||||||
constant
|
let ty = self.monomorphize(constant.ty());
|
||||||
|
let val = self
|
||||||
|
.eval_unevaluated_mir_constant_to_valtree(constant)
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
.map(|val| {
|
.map(|val| {
|
||||||
let field_ty = ty.builtin_index().unwrap();
|
let field_ty = ty.builtin_index().unwrap();
|
||||||
let c = mir::ConstantKind::from_value(val, ty);
|
let values: Vec<_> = val
|
||||||
let values: Vec<_> = bx
|
.unwrap_branch()
|
||||||
.tcx()
|
|
||||||
.destructure_mir_constant(ty::ParamEnv::reveal_all(), c)
|
|
||||||
.fields
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| {
|
.map(|field| {
|
||||||
if let Some(prim) = field.try_to_scalar() {
|
if let Some(prim) = field.try_to_scalar() {
|
||||||
|
@ -88,15 +105,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let llval = bx.const_struct(&values, false);
|
bx.const_struct(&values, false)
|
||||||
(llval, c.ty())
|
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|| {
|
||||||
bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span });
|
bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span: constant.span });
|
||||||
// We've errored, so we don't have to produce working code.
|
// We've errored, so we don't have to produce working code.
|
||||||
let ty = self.monomorphize(ty);
|
|
||||||
let llty = bx.backend_type(bx.layout_of(ty));
|
let llty = bx.backend_type(bx.layout_of(ty));
|
||||||
(bx.const_undef(llty), ty)
|
bx.const_undef(llty)
|
||||||
})
|
});
|
||||||
|
(val, ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,6 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
val: mir::ConstantKind<'tcx>,
|
val: mir::ConstantKind<'tcx>,
|
||||||
) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
|
) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
|
||||||
trace!("destructure_mir_constant: {:?}", val);
|
|
||||||
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
||||||
let op = ecx.eval_mir_constant(&val, None, None)?;
|
let op = ecx.eval_mir_constant(&val, None, None)?;
|
||||||
|
|
||||||
|
|
|
@ -95,11 +95,15 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
// used generic parameters is a bug of evaluation, so checking for it
|
// used generic parameters is a bug of evaluation, so checking for it
|
||||||
// here does feel somewhat sensible.
|
// here does feel somewhat sensible.
|
||||||
if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
|
if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
|
||||||
assert!(matches!(
|
let def_kind = self.def_kind(instance.def_id());
|
||||||
self.def_kind(ct.def),
|
assert!(
|
||||||
DefKind::InlineConst | DefKind::AnonConst
|
matches!(
|
||||||
));
|
def_kind,
|
||||||
let mir_body = self.mir_for_ctfe(ct.def);
|
DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
|
||||||
|
),
|
||||||
|
"{cid:?} is {def_kind:?}",
|
||||||
|
);
|
||||||
|
let mir_body = self.mir_for_ctfe(instance.def_id());
|
||||||
if mir_body.is_polymorphic {
|
if mir_body.is_polymorphic {
|
||||||
let Some(local_def_id) = ct.def.as_local() else { return };
|
let Some(local_def_id) = ct.def.as_local() else { return };
|
||||||
self.struct_span_lint_hir(
|
self.struct_span_lint_hir(
|
||||||
|
@ -239,15 +243,3 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
|
||||||
self.eval_to_allocation_raw(param_env.and(gid))
|
self.eval_to_allocation_raw(param_env.and(gid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> TyCtxt<'tcx> {
|
|
||||||
/// Destructure a mir constant ADT or array into its variant index and its field values.
|
|
||||||
/// Panics if the destructuring fails, use `try_destructure_mir_constant` for fallible version.
|
|
||||||
pub fn destructure_mir_constant(
|
|
||||||
self,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
|
||||||
constant: mir::ConstantKind<'tcx>,
|
|
||||||
) -> mir::DestructuredConstant<'tcx> {
|
|
||||||
self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2581,10 +2581,9 @@ pub struct UnevaluatedConst<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> UnevaluatedConst<'tcx> {
|
impl<'tcx> UnevaluatedConst<'tcx> {
|
||||||
// FIXME: probably should get rid of this method. It's also wrong to
|
|
||||||
// shrink and then later expand a promoted.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
|
pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
|
||||||
|
assert_eq!(self.promoted, None);
|
||||||
ty::UnevaluatedConst { def: self.def, substs: self.substs }
|
ty::UnevaluatedConst { def: self.def, substs: self.substs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,14 @@ use rustc_hir::{
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
use rustc_hir_analysis::hir_ty_to_ty;
|
||||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir::interpret::ErrorHandled;
|
||||||
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
|
|
||||||
use rustc_middle::ty::adjustment::Adjust;
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::{sym, InnerSpan, Span};
|
use rustc_span::{sym, InnerSpan, Span};
|
||||||
|
use rustc_target::abi::VariantIdx;
|
||||||
|
use rustc_middle::mir::interpret::EvalToValTreeResult;
|
||||||
|
use rustc_middle::mir::interpret::GlobalId;
|
||||||
|
|
||||||
// FIXME: this is a correctness problem but there's no suitable
|
// FIXME: this is a correctness problem but there's no suitable
|
||||||
// warn-by-default category.
|
// warn-by-default category.
|
||||||
|
@ -141,21 +143,35 @@ fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
|
|
||||||
fn is_value_unfrozen_raw<'tcx>(
|
fn is_value_unfrozen_raw<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
result: Result<ConstValue<'tcx>, ErrorHandled>,
|
result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
fn inner<'tcx>(cx: &LateContext<'tcx>, val: mir::ConstantKind<'tcx>) -> bool {
|
fn inner<'tcx>(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
match val.ty().kind() {
|
match *ty.kind() {
|
||||||
// the fact that we have to dig into every structs to search enums
|
// the fact that we have to dig into every structs to search enums
|
||||||
// leads us to the point checking `UnsafeCell` directly is the only option.
|
// leads us to the point checking `UnsafeCell` directly is the only option.
|
||||||
ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
|
ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
|
||||||
// As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
|
// As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
|
||||||
// contained value.
|
// contained value.
|
||||||
ty::Adt(def, ..) if def.is_union() => false,
|
ty::Adt(def, ..) if def.is_union() => false,
|
||||||
ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
|
ty::Array(ty, _) => {
|
||||||
let val = cx.tcx.destructure_mir_constant(cx.param_env, val);
|
val.unwrap_branch().iter().any(|field| inner(cx, *field, ty))
|
||||||
val.fields.iter().any(|field| inner(cx, *field))
|
|
||||||
},
|
},
|
||||||
|
ty::Adt(def, _) if def.is_union() => false,
|
||||||
|
ty::Adt(def, substs) if def.is_enum() => {
|
||||||
|
let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
|
||||||
|
let variant_index =
|
||||||
|
VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
|
||||||
|
fields.iter().copied().zip(
|
||||||
|
def.variants()[variant_index]
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, field, ty))
|
||||||
|
}
|
||||||
|
ty::Adt(def, substs) => {
|
||||||
|
val.unwrap_branch().iter().zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, *field, ty))
|
||||||
|
}
|
||||||
|
ty::Tuple(tys) => val.unwrap_branch().iter().zip(tys).any(|(field, ty)| inner(cx, *field, ty)),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,24 +200,44 @@ fn is_value_unfrozen_raw<'tcx>(
|
||||||
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
|
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
|
||||||
err == ErrorHandled::TooGeneric
|
err == ErrorHandled::TooGeneric
|
||||||
},
|
},
|
||||||
|val| inner(cx, mir::ConstantKind::from_value(val, ty)),
|
|val| val.map_or(true, |val| inner(cx, val, ty)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
|
fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
|
||||||
let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id());
|
let def_id = body_id.hir_id.owner.to_def_id();
|
||||||
|
let substs = ty::InternalSubsts::identity_for_item(cx.tcx, def_id);
|
||||||
|
let instance = ty::Instance::new(def_id, substs);
|
||||||
|
let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
|
||||||
|
let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
|
||||||
|
let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None);
|
||||||
is_value_unfrozen_raw(cx, result, ty)
|
is_value_unfrozen_raw(cx, result, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
|
fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
|
||||||
let substs = cx.typeck_results().node_substs(hir_id);
|
let substs = cx.typeck_results().node_substs(hir_id);
|
||||||
|
|
||||||
let result = cx
|
let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, substs), None);
|
||||||
.tcx
|
|
||||||
.const_eval_resolve(cx.param_env, mir::UnevaluatedConst::new(def_id, substs), None);
|
|
||||||
is_value_unfrozen_raw(cx, result, ty)
|
is_value_unfrozen_raw(cx, result, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn const_eval_resolve<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
ct: ty::UnevaluatedConst<'tcx>,
|
||||||
|
span: Option<Span>,
|
||||||
|
) -> EvalToValTreeResult<'tcx> {
|
||||||
|
match ty::Instance::resolve(tcx, param_env, ct.def, ct.substs) {
|
||||||
|
Ok(Some(instance)) => {
|
||||||
|
let cid = GlobalId { instance, promoted: None };
|
||||||
|
tcx.const_eval_global_id_for_typeck(param_env, cid, span)
|
||||||
|
}
|
||||||
|
Ok(None) => Err(ErrorHandled::TooGeneric),
|
||||||
|
Err(err) => Err(ErrorHandled::Reported(err.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum Source {
|
enum Source {
|
||||||
Item { item: Span },
|
Item { item: Span },
|
||||||
|
|
12
src/tools/clippy/tests/ui/crashes/ice-9445.stderr
Normal file
12
src/tools/clippy/tests/ui/crashes/ice-9445.stderr
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
error: a `const` item should never be interior mutable
|
||||||
|
--> $DIR/ice-9445.rs:1:1
|
||||||
|
|
|
||||||
|
LL | const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit();
|
||||||
|
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| make this a static item (maybe with lazy_static)
|
||||||
|
|
|
||||||
|
= note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Reference in a new issue