Skip to content

Commit

Permalink
Unrolled build for rust-lang#136180
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#136180 - lukas-code:typed-valtree, r=oli-obk

Introduce a wrapper for "typed valtrees" and properly check the type before extracting the value

This PR adds a new wrapper type `ty::Value` to replace the tuple `(Ty, ty::ValTree)` and become the new canonical representation of type-level constant values.

The value extraction methods `try_to_bits`/`try_to_bool`/`try_to_target_usize` are moved to this new type. For `try_to_bits` in particular, this avoids some redundant matches on `ty::ConstKind::Value`. Furthermore, these methods and will now properly check the type before extracting the value, which fixes some ICEs.

The name `ty::Value` was chosen to be consistent with `ty::Expr`.

Commit 1 should be non-functional and commit 2 adds the type check.

---

fixes rust-lang#131102
supercedes rust-lang#136130

r? `@oli-obk`
cc `@FedericoBruzzone` `@BoxyUwU`
  • Loading branch information
rust-timer authored Jan 31, 2025
2 parents a730edc + ca3ff83 commit 77215d3
Show file tree
Hide file tree
Showing 50 changed files with 296 additions and 225 deletions.
7 changes: 1 addition & 6 deletions compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
return;
}

let idx = generic_args[2]
.expect_const()
.try_to_valtree()
.expect("expected monomorphic const in codegen")
.0
.unwrap_branch();
let idx = generic_args[2].expect_const().to_value().valtree.unwrap_branch();

assert_eq!(x.layout(), y.layout());
let layout = x.layout();
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1329,7 +1329,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}

if name == sym::simd_shuffle_generic {
let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
let n = idx.len() as u64;

let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
Expand Down
16 changes: 6 additions & 10 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,25 +673,23 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
ty::ConstKind::Param(param) => {
write!(output, "{}", param.name)
}
ty::ConstKind::Value(ty, valtree) => {
match ty.kind() {
ty::ConstKind::Value(cv) => {
match cv.ty.kind() {
ty::Int(ity) => {
// FIXME: directly extract the bits from a valtree instead of evaluating an
// already evaluated `Const` in order to get the bits.
let bits = ct
let bits = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in codegen");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
write!(output, "{val}")
}
ty::Uint(_) => {
let val = ct
let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in codegen");
write!(output, "{val}")
}
ty::Bool => {
let val = ct.try_to_bool().expect("expected monomorphic const in codegen");
let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
write!(output, "{val}")
}
_ => {
Expand All @@ -703,9 +701,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
// avoiding collisions and will make the emitted type names shorter.
let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
let mut hasher = StableHasher::new();
hcx.while_hashing_spans(false, |hcx| {
(ty, valtree).hash_stable(hcx, &mut hasher)
});
hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
hasher.finish::<Hash64>()
});

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
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 simd_shuffle (passing as argument instead of as a generic param).
rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)),
rustc_type_ir::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)),
other => span_bug!(constant.span, "{other:#?}"),
},
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ where
Const::Ty(_, ct)
if matches!(
ct.kind(),
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _)
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
) =>
{
None
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ pub(crate) fn eval_to_valtree<'tcx>(

/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
/// construction has finished.
// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
// FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
#[instrument(skip(tcx), level = "debug", ret)]
pub fn valtree_to_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_const_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,13 @@ pub fn provide(providers: &mut Providers) {
};
providers.hooks.try_destructure_mir_constant_for_user_output =
const_eval::try_destructure_mir_constant_for_user_output;
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree)
providers.valtree_to_const_val = |tcx, cv| {
const_eval::valtree_to_const_value(
tcx,
ty::TypingEnv::fully_monomorphized(),
cv.ty,
cv.valtree,
)
};
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/freshen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
}

ty::ConstKind::Param(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Value(_)
| ty::ConstKind::Unevaluated(..)
| ty::ConstKind::Expr(..)
| ty::ConstKind::Error(_) => ct.super_fold_with(self),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ impl<'tcx> InferCtxt<'tcx> {
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
| ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Value(_)
| ty::ConstKind::Error(_)
| ty::ConstKind::Expr(_) => ct,
}
Expand Down
22 changes: 10 additions & 12 deletions compiler/rustc_middle/src/mir/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ impl<'tcx> Const<'tcx> {
// Dont use the outer ty as on invalid code we can wind up with them not being the same.
// this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir
// was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
ty::ConstKind::Value(ty, _) => ty,
ty::ConstKind::Value(cv) => cv.ty,
_ => *ty,
}
}
Expand All @@ -264,7 +264,7 @@ impl<'tcx> Const<'tcx> {
pub fn is_required_const(&self) -> bool {
match self {
Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(_, _) => false, // already a value, cannot error
ty::ConstKind::Value(_) => false, // already a value, cannot error
_ => true,
},
Const::Val(..) => false, // already a value, cannot error
Expand All @@ -276,11 +276,11 @@ impl<'tcx> Const<'tcx> {
pub fn try_to_scalar(self) -> Option<Scalar> {
match self {
Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
// A valtree of a type where leaves directly represent the scalar const value.
// Just checking whether it is a leaf is insufficient as e.g. references are leafs
// but the leaf value is the value they point to, not the reference itself!
Some(valtree.unwrap_leaf().into())
Some(cv.valtree.unwrap_leaf().into())
}
_ => None,
},
Expand All @@ -295,9 +295,7 @@ impl<'tcx> Const<'tcx> {
match self {
Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
Some(valtree.unwrap_leaf())
}
ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
_ => None,
},
_ => None,
Expand Down Expand Up @@ -328,7 +326,7 @@ impl<'tcx> Const<'tcx> {
}

match c.kind() {
ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))),
ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
ConstKind::Expr(_) => {
bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
}
Expand All @@ -353,13 +351,13 @@ impl<'tcx> Const<'tcx> {
typing_env: ty::TypingEnv<'tcx>,
) -> Option<Scalar> {
if let Const::Ty(_, c) = self
&& let ty::ConstKind::Value(ty, val) = c.kind()
&& ty.is_primitive()
&& let ty::ConstKind::Value(cv) = c.kind()
&& cv.ty.is_primitive()
{
// Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
// are valtree leaves, and *not* on references. (References should return the
// pointer here, which valtrees don't represent.)
Some(val.unwrap_leaf().into())
Some(cv.valtree.unwrap_leaf().into())
} else {
self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
}
Expand Down Expand Up @@ -473,7 +471,7 @@ impl<'tcx> Const<'tcx> {
// A valtree may be a reference. Valtree references correspond to a
// different allocation each time they are evaluated. Valtrees for primitive
// types are fine though.
ty::ConstKind::Value(ty, _) => ty.is_primitive(),
ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
// This can happen if evaluation of a constant failed. The result does not matter
// much since compilation is doomed.
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
ty::ConstKind::Unevaluated(uv) => {
format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
}
ty::ConstKind::Value(_, val) => format!("ty::Valtree({})", fmt_valtree(&val)),
ty::ConstKind::Value(cv) => {
format!("ty::Valtree({})", fmt_valtree(&cv.valtree))
}
// No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
ty::ConstKind::Error(_) => "Error".to_string(),
// These variants shouldn't exist in the MIR.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/query/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
}
}

impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
impl<'tcx> Key for ty::Value<'tcx> {
type Cache<V> = DefaultCache<Self, V>;

fn default_span(&self, _: TyCtxt<'_>) -> Span {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,9 +1256,9 @@ rustc_queries! {
desc { "evaluating type-level constant" }
}

/// Converts a type level constant value into `ConstValue`
query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> {
desc { "converting type-level constant value to mir constant value"}
/// Converts a type-level constant value into a MIR constant value.
query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
desc { "converting type-level constant value to MIR constant value"}
}

/// Destructures array, ADT or tuple constants into the constants
Expand Down
47 changes: 15 additions & 32 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use rustc_error_messages::MultiSpan;
use rustc_macros::HashStable;
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};

use crate::mir::interpret::Scalar;
use crate::ty::{self, Ty, TyCtxt};

mod int;
Expand Down Expand Up @@ -110,8 +109,8 @@ impl<'tcx> Const<'tcx> {
}

#[inline]
pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
Const::new(tcx, ty::ConstKind::Value(ty, val))
pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree }))
}

#[inline]
Expand Down Expand Up @@ -214,47 +213,31 @@ impl<'tcx> Const<'tcx> {
Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize)
}

/// Panics if self.kind != ty::ConstKind::Value
pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
/// Panics if `self.kind != ty::ConstKind::Value`.
pub fn to_value(self) -> ty::Value<'tcx> {
match self.kind() {
ty::ConstKind::Value(ty, valtree) => (valtree, ty),
ty::ConstKind::Value(cv) => cv,
_ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
}
}

/// Attempts to convert to a `ValTree`
pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> {
/// Attempts to convert to a value.
///
/// Note that this does not evaluate the constant.
pub fn try_to_value(self) -> Option<ty::Value<'tcx>> {
match self.kind() {
ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)),
ty::ConstKind::Value(cv) => Some(cv),
_ => None,
}
}

#[inline]
pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> {
let (valtree, ty) = self.try_to_valtree()?;
Some((valtree.try_to_scalar()?, ty))
}

pub fn try_to_bool(self) -> Option<bool> {
self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok()
}

/// Convenience method to extract the value of a usize constant,
/// useful to get the length of an array type.
///
/// Note that this does not evaluate the constant.
#[inline]
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_valtree()?.0.try_to_target_usize(tcx)
}

/// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
/// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
/// contains const generic parameters or pointers).
#[inline]
pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
let (scalar, ty) = self.try_to_scalar()?;
let scalar = scalar.try_to_scalar_int().ok()?;
let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(ty);
let size = tcx.layout_of(input).ok()?.size;
Some(scalar.to_bits(size))
self.try_to_value()?.try_to_target_usize(tcx)
}

pub fn is_ct_infer(self) -> bool {
Expand Down
Loading

0 comments on commit 77215d3

Please sign in to comment.