valtree construction: keep track of which type was valtree-incompatible
This commit is contained in:
parent
52f3c71c8d
commit
fa74a9e6aa
11 changed files with 37 additions and 35 deletions
|
@ -37,13 +37,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
pub fn eval_unevaluated_mir_constant_to_valtree(
|
pub fn eval_unevaluated_mir_constant_to_valtree(
|
||||||
&self,
|
&self,
|
||||||
constant: &mir::ConstOperand<'tcx>,
|
constant: &mir::ConstOperand<'tcx>,
|
||||||
) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> {
|
) -> Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled> {
|
||||||
let uv = match self.monomorphize(constant.const_) {
|
let uv = match self.monomorphize(constant.const_) {
|
||||||
mir::Const::Unevaluated(uv, _) => uv.shrink(),
|
mir::Const::Unevaluated(uv, _) => uv.shrink(),
|
||||||
mir::Const::Ty(_, c) => match c.kind() {
|
mir::Const::Ty(_, c) => match c.kind() {
|
||||||
// A constant that came from a const generic but was then used as an argument to old-style
|
// A constant that came from a const generic but was then used as an argument to old-style
|
||||||
// simd_shuffle (passing as argument instead of as a generic param).
|
// simd_shuffle (passing as argument instead of as a generic param).
|
||||||
rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Some(valtree)),
|
rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)),
|
||||||
other => span_bug!(constant.span, "{other:#?}"),
|
other => span_bug!(constant.span, "{other:#?}"),
|
||||||
},
|
},
|
||||||
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
|
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
|
||||||
|
@ -70,6 +70,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
let val = self
|
let val = self
|
||||||
.eval_unevaluated_mir_constant_to_valtree(constant)
|
.eval_unevaluated_mir_constant_to_valtree(constant)
|
||||||
.ok()
|
.ok()
|
||||||
|
.map(|x| x.ok())
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|val| {
|
.map(|val| {
|
||||||
let field_ty = ty.builtin_index().unwrap();
|
let field_ty = ty.builtin_index().unwrap();
|
||||||
|
|
|
@ -27,15 +27,15 @@ pub(crate) use valtrees::{eval_to_valtree, valtree_to_const_value};
|
||||||
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
|
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
|
||||||
const VALTREE_MAX_NODES: usize = 100000;
|
const VALTREE_MAX_NODES: usize = 100000;
|
||||||
|
|
||||||
pub(crate) enum ValTreeCreationError {
|
pub(crate) enum ValTreeCreationError<'tcx> {
|
||||||
NodesOverflow,
|
NodesOverflow,
|
||||||
/// Values of this type, or this particular value, are not supported as valtrees.
|
/// Values of this type, or this particular value, are not supported as valtrees.
|
||||||
NonSupportedType,
|
NonSupportedType(Ty<'tcx>),
|
||||||
}
|
}
|
||||||
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError>;
|
pub(crate) type ValTreeCreationResult<'tcx> = Result<ty::ValTree<'tcx>, ValTreeCreationError<'tcx>>;
|
||||||
|
|
||||||
impl From<InterpErrorInfo<'_>> for ValTreeCreationError {
|
impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> {
|
||||||
fn from(err: InterpErrorInfo<'_>) -> Self {
|
fn from(err: InterpErrorInfo<'tcx>) -> Self {
|
||||||
ty::tls::with(|tcx| {
|
ty::tls::with(|tcx| {
|
||||||
bug!(
|
bug!(
|
||||||
"Unexpected Undefined Behavior error during valtree construction: {}",
|
"Unexpected Undefined Behavior error during valtree construction: {}",
|
||||||
|
|
|
@ -120,13 +120,13 @@ fn const_to_valtree_inner<'tcx>(
|
||||||
// We could allow wide raw pointers where both sides are integers in the future,
|
// We could allow wide raw pointers where both sides are integers in the future,
|
||||||
// but for now we reject them.
|
// but for now we reject them.
|
||||||
if matches!(val.layout.abi, Abi::ScalarPair(..)) {
|
if matches!(val.layout.abi, Abi::ScalarPair(..)) {
|
||||||
return Err(ValTreeCreationError::NonSupportedType);
|
return Err(ValTreeCreationError::NonSupportedType(ty));
|
||||||
}
|
}
|
||||||
let val = val.to_scalar();
|
let val = val.to_scalar();
|
||||||
// We are in the CTFE machine, so ptr-to-int casts will fail.
|
// We are in the CTFE machine, so ptr-to-int casts will fail.
|
||||||
// This can only be `Ok` if `val` already is an integer.
|
// This can only be `Ok` if `val` already is an integer.
|
||||||
let Ok(val) = val.try_to_scalar_int() else {
|
let Ok(val) = val.try_to_scalar_int() else {
|
||||||
return Err(ValTreeCreationError::NonSupportedType);
|
return Err(ValTreeCreationError::NonSupportedType(ty));
|
||||||
};
|
};
|
||||||
// It's just a ScalarInt!
|
// It's just a ScalarInt!
|
||||||
Ok(ty::ValTree::Leaf(val))
|
Ok(ty::ValTree::Leaf(val))
|
||||||
|
@ -134,7 +134,7 @@ fn const_to_valtree_inner<'tcx>(
|
||||||
|
|
||||||
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
|
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
|
||||||
// agree with runtime equality tests.
|
// agree with runtime equality tests.
|
||||||
ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType),
|
ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType(ty)),
|
||||||
|
|
||||||
ty::Ref(_, _, _) => {
|
ty::Ref(_, _, _) => {
|
||||||
let derefd_place = ecx.deref_pointer(place)?;
|
let derefd_place = ecx.deref_pointer(place)?;
|
||||||
|
@ -148,7 +148,7 @@ fn const_to_valtree_inner<'tcx>(
|
||||||
// resolving their backing type, even if we can do that at const eval time. We may
|
// resolving their backing type, even if we can do that at const eval time. We may
|
||||||
// hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future,
|
// hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future,
|
||||||
// but it is unclear if this is useful.
|
// but it is unclear if this is useful.
|
||||||
ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType),
|
ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType(ty)),
|
||||||
|
|
||||||
ty::Tuple(elem_tys) => {
|
ty::Tuple(elem_tys) => {
|
||||||
branches(ecx, place, elem_tys.len(), None, num_nodes)
|
branches(ecx, place, elem_tys.len(), None, num_nodes)
|
||||||
|
@ -156,7 +156,7 @@ fn const_to_valtree_inner<'tcx>(
|
||||||
|
|
||||||
ty::Adt(def, _) => {
|
ty::Adt(def, _) => {
|
||||||
if def.is_union() {
|
if def.is_union() {
|
||||||
return Err(ValTreeCreationError::NonSupportedType);
|
return Err(ValTreeCreationError::NonSupportedType(ty));
|
||||||
} else if def.variants().is_empty() {
|
} else if def.variants().is_empty() {
|
||||||
bug!("uninhabited types should have errored and never gotten converted to valtree")
|
bug!("uninhabited types should have errored and never gotten converted to valtree")
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ fn const_to_valtree_inner<'tcx>(
|
||||||
| ty::Closure(..)
|
| ty::Closure(..)
|
||||||
| ty::CoroutineClosure(..)
|
| ty::CoroutineClosure(..)
|
||||||
| ty::Coroutine(..)
|
| ty::Coroutine(..)
|
||||||
| ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType),
|
| ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType(ty)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
||||||
let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
|
let valtree_result = const_to_valtree_inner(&ecx, &place, &mut num_nodes);
|
||||||
|
|
||||||
match valtree_result {
|
match valtree_result {
|
||||||
Ok(valtree) => Ok(Some(valtree)),
|
Ok(valtree) => Ok(Ok(valtree)),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let did = cid.instance.def_id();
|
let did = cid.instance.def_id();
|
||||||
let global_const_id = cid.display(tcx);
|
let global_const_id = cid.display(tcx);
|
||||||
|
@ -262,7 +262,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
||||||
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
|
tcx.dcx().emit_err(MaxNumNodesInConstErr { span, global_const_id });
|
||||||
Err(handled.into())
|
Err(handled.into())
|
||||||
}
|
}
|
||||||
ValTreeCreationError::NonSupportedType => Ok(None),
|
ValTreeCreationError::NonSupportedType(ty) => Ok(Err(ty)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1427,17 +1427,17 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<ty::Const<'tcx>, ErrorHandled> {
|
) -> Result<ty::Const<'tcx>, ErrorHandled> {
|
||||||
match self.const_eval_resolve(param_env, unevaluated, span) {
|
match self.const_eval_resolve(param_env, unevaluated, span) {
|
||||||
Ok(Some(val)) => Ok(ty::Const::new_value(
|
Ok(Ok(val)) => Ok(ty::Const::new_value(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
val,
|
val,
|
||||||
self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args),
|
self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args),
|
||||||
)),
|
)),
|
||||||
Ok(None) => {
|
Ok(Err(bad_ty)) => {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let def_id = unevaluated.def;
|
let def_id = unevaluated.def;
|
||||||
span_bug!(
|
span_bug!(
|
||||||
tcx.def_span(def_id),
|
tcx.def_span(def_id),
|
||||||
"unable to construct a constant value for the unevaluated constant {:?}",
|
"unable to construct a valtree for the unevaluated constant {:?}: type {bad_ty} is not valtree-compatible",
|
||||||
unevaluated
|
unevaluated
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,11 @@ TrivialTypeTraversalImpls! { ErrorHandled }
|
||||||
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
|
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
|
||||||
pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
|
pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
|
||||||
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
|
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
|
||||||
/// `Ok(None)` indicates the constant was fine, but the valtree couldn't be constructed.
|
/// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed
|
||||||
/// This is needed in `thir::pattern::lower_inline_const`.
|
/// because the value containts something of type `ty` that is not valtree-compatible.
|
||||||
pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
|
/// The caller can then show an appropriate error; the query does not have the
|
||||||
|
/// necssary context to give good user-facing errors for this case.
|
||||||
|
pub type EvalToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
|
rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
|
||||||
|
|
|
@ -157,9 +157,10 @@ impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
|
||||||
type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
|
type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EraseType for Result<Option<ty::ValTree<'_>>, mir::interpret::ErrorHandled> {
|
impl EraseType for Result<Result<ty::ValTree<'_>, Ty<'_>>, mir::interpret::ErrorHandled> {
|
||||||
type Result =
|
type Result = [u8; size_of::<
|
||||||
[u8; size_of::<Result<Option<ty::ValTree<'static>>, mir::interpret::ErrorHandled>>()];
|
Result<Result<ty::ValTree<'static>, Ty<'static>>, mir::interpret::ErrorHandled>,
|
||||||
|
>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EraseType for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
|
impl EraseType for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
|
||||||
|
|
|
@ -328,8 +328,7 @@ impl<'tcx> Const<'tcx> {
|
||||||
let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env);
|
let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env);
|
||||||
// try to resolve e.g. associated constants to their definition on an impl, and then
|
// try to resolve e.g. associated constants to their definition on an impl, and then
|
||||||
// evaluate the const.
|
// evaluate the const.
|
||||||
let Some(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)?
|
let Ok(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? else {
|
||||||
else {
|
|
||||||
// This can happen when we run on ill-typed code.
|
// This can happen when we run on ill-typed code.
|
||||||
let e = tcx.dcx().span_delayed_bug(
|
let e = tcx.dcx().span_delayed_bug(
|
||||||
span,
|
span,
|
||||||
|
|
|
@ -581,8 +581,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||||
.tcx
|
.tcx
|
||||||
.const_eval_global_id_for_typeck(param_env_reveal_all, cid, span)
|
.const_eval_global_id_for_typeck(param_env_reveal_all, cid, span)
|
||||||
.map(|val| match val {
|
.map(|val| match val {
|
||||||
Some(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
|
Ok(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
|
||||||
None => mir::Const::Val(
|
Err(_) => mir::Const::Val(
|
||||||
self.tcx
|
self.tcx
|
||||||
.const_eval_global_id(param_env_reveal_all, cid, span)
|
.const_eval_global_id(param_env_reveal_all, cid, span)
|
||||||
.expect("const_eval_global_id_for_typeck should have already failed"),
|
.expect("const_eval_global_id_for_typeck should have already failed"),
|
||||||
|
@ -682,8 +682,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||||
// First try using a valtree in order to destructure the constant into a pattern.
|
// First try using a valtree in order to destructure the constant into a pattern.
|
||||||
// FIXME: replace "try to do a thing, then fall back to another thing"
|
// FIXME: replace "try to do a thing, then fall back to another thing"
|
||||||
// but something more principled, like a trait query checking whether this can be turned into a valtree.
|
// but something more principled, like a trait query checking whether this can be turned into a valtree.
|
||||||
if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span)
|
if let Ok(Ok(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) {
|
||||||
{
|
|
||||||
let subpattern = self.const_to_pat(
|
let subpattern = self.const_to_pat(
|
||||||
Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
|
Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -87,12 +87,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
||||||
) -> Option<ty::Const<'tcx>> {
|
) -> Option<ty::Const<'tcx>> {
|
||||||
use rustc_middle::mir::interpret::ErrorHandled;
|
use rustc_middle::mir::interpret::ErrorHandled;
|
||||||
match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
|
match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
|
||||||
Ok(Some(val)) => Some(ty::Const::new_value(
|
Ok(Ok(val)) => Some(ty::Const::new_value(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
val,
|
val,
|
||||||
self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args),
|
self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args),
|
||||||
)),
|
)),
|
||||||
Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None,
|
Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None,
|
||||||
Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())),
|
Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -765,8 +765,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
||||||
unevaluated,
|
unevaluated,
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
) {
|
) {
|
||||||
Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))),
|
Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))),
|
||||||
Ok(None) => {
|
Ok(Err(_)) => {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let reported =
|
let reported =
|
||||||
tcx.dcx().emit_err(UnableToConstructConstantValue {
|
tcx.dcx().emit_err(UnableToConstructConstantValue {
|
||||||
|
|
|
@ -235,7 +235,7 @@ impl<'tcx> NonCopyConst<'tcx> {
|
||||||
|
|
||||||
fn is_value_unfrozen_raw(
|
fn is_value_unfrozen_raw(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
|
result: Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
result.map_or_else(
|
result.map_or_else(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue