Rollup merge of #150411 - Kivooeo:somefunnystuff, r=BoxyUwU

refactor `destructure_const`

r? BoxyUwU

as you suggested yesterday (will add more context like links when it stops being a draft)

tried to split this into some meaningful commits hope it help ^^
This commit is contained in:
Jonathan Brouwer 2025-12-28 22:52:33 +01:00 committed by GitHub
commit 1117bd9e78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 174 additions and 159 deletions

View file

@ -1896,7 +1896,8 @@ fn pretty_print_const_value_tcx<'tcx>(
// Aggregates, printed as array/tuple/struct/variant construction syntax.
//
// NB: the `has_non_region_param` check ensures that we can use
// the `destructure_const` query with an empty `ty::ParamEnv` without
// the `try_destructure_mir_constant_for_user_output ` query with
// an empty `TypingEnv::fully_monomorphized` without
// introducing ICEs (e.g. via `layout_of`) from missing bounds.
// E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
// to be able to destructure the tuple into `(0u8, *mut T)`

View file

@ -402,7 +402,7 @@ tcx_lifetime! {
rustc_middle::ty::ClauseKind,
rustc_middle::ty::ClosureTypeInfo,
rustc_middle::ty::Const,
rustc_middle::ty::DestructuredConst,
rustc_middle::ty::DestructuredAdtConst,
rustc_middle::ty::ExistentialTraitRef,
rustc_middle::ty::FnSig,
rustc_middle::ty::GenericArg,

View file

@ -1408,12 +1408,6 @@ rustc_queries! {
desc { "converting type-level constant value to MIR constant value"}
}
/// Destructures array, ADT or tuple constants into the constants
/// of their fields.
query destructure_const(key: ty::Const<'tcx>) -> ty::DestructuredConst<'tcx> {
desc { "destructuring type level constant"}
}
// FIXME get rid of this with valtrees
query lit_to_const(
key: LitToConstInput<'tcx>

View file

@ -1,6 +1,7 @@
use std::fmt;
use std::ops::Deref;
use rustc_abi::{FIRST_VARIANT, VariantIdx};
use rustc_data_structures::intern::Interned;
use rustc_hir::def::Namespace;
use rustc_macros::{
@ -189,6 +190,26 @@ impl<'tcx> Value<'tcx> {
ValTreeKind::Leaf(_) => None,
}
}
/// Destructures array, ADT or tuple constants into the constants
/// of their fields.
pub fn destructure_adt_const(&self) -> ty::DestructuredAdtConst<'tcx> {
let fields = self.to_branch();
let (variant, fields) = match self.ty.kind() {
ty::Adt(def, _) if def.variants().is_empty() => {
bug!("unreachable")
}
ty::Adt(def, _) if def.is_enum() => {
let (head, rest) = fields.split_first().unwrap();
(VariantIdx::from_u32(head.to_leaf().to_u32()), rest)
}
ty::Adt(_, _) => (FIRST_VARIANT, fields),
_ => bug!("destructure_adt_const called on non-ADT type: {:?}", self.ty),
};
ty::DestructuredAdtConst { variant, fields }
}
}
impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {

View file

@ -2331,8 +2331,8 @@ impl<'tcx> fmt::Debug for SymbolName<'tcx> {
/// The constituent parts of a type level constant of kind ADT or array.
#[derive(Copy, Clone, Debug, HashStable)]
pub struct DestructuredConst<'tcx> {
pub variant: Option<VariantIdx>,
pub struct DestructuredAdtConst<'tcx> {
pub variant: VariantIdx,
pub fields: &'tcx [ty::Const<'tcx>],
}

View file

@ -1919,66 +1919,65 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.pretty_print_byte_str(bytes)?;
return Ok(());
}
// Aggregates, printed as array/tuple/struct/variant construction syntax.
(ty::ValTreeKind::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => {
let contents = self.tcx().destructure_const(ty::Const::new_value(
self.tcx(),
cv.valtree,
cv.ty,
));
let fields = contents.fields.iter().copied();
(ty::ValTreeKind::Branch(fields), ty::Array(..) | ty::Tuple(..)) => {
let fields_iter = fields.iter().copied();
match *cv.ty.kind() {
ty::Array(..) => {
write!(self, "[")?;
self.comma_sep(fields)?;
self.comma_sep(fields_iter)?;
write!(self, "]")?;
}
ty::Tuple(..) => {
write!(self, "(")?;
self.comma_sep(fields)?;
if contents.fields.len() == 1 {
self.comma_sep(fields_iter)?;
if fields.len() == 1 {
write!(self, ",")?;
}
write!(self, ")")?;
}
ty::Adt(def, _) if def.variants().is_empty() => {
self.typed_value(
|this| {
write!(this, "unreachable()")?;
Ok(())
},
|this| this.print_type(cv.ty),
": ",
)?;
}
ty::Adt(def, args) => {
let variant_idx =
contents.variant.expect("destructed const of adt without variant idx");
let variant_def = &def.variant(variant_idx);
self.pretty_print_value_path(variant_def.def_id, args)?;
match variant_def.ctor_kind() {
Some(CtorKind::Const) => {}
Some(CtorKind::Fn) => {
write!(self, "(")?;
self.comma_sep(fields)?;
write!(self, ")")?;
}
None => {
write!(self, " {{ ")?;
let mut first = true;
for (field_def, field) in iter::zip(&variant_def.fields, fields) {
if !first {
write!(self, ", ")?;
}
write!(self, "{}: ", field_def.name)?;
field.print(self)?;
first = false;
_ => unreachable!(),
}
return Ok(());
}
(ty::ValTreeKind::Branch(_), ty::Adt(def, args)) => {
let contents = cv.destructure_adt_const();
let fields = contents.fields.iter().copied();
if def.variants().is_empty() {
self.typed_value(
|this| {
write!(this, "unreachable()")?;
Ok(())
},
|this| this.print_type(cv.ty),
": ",
)?;
} else {
let variant_idx = contents.variant;
let variant_def = &def.variant(variant_idx);
self.pretty_print_value_path(variant_def.def_id, args)?;
match variant_def.ctor_kind() {
Some(CtorKind::Const) => {}
Some(CtorKind::Fn) => {
write!(self, "(")?;
self.comma_sep(fields)?;
write!(self, ")")?;
}
None => {
write!(self, " {{ ")?;
let mut first = true;
for (field_def, field) in iter::zip(&variant_def.fields, fields) {
if !first {
write!(self, ", ")?;
}
write!(self, " }}")?;
write!(self, "{}: ", field_def.name)?;
field.print(self)?;
first = false;
}
write!(self, " }}")?;
}
}
_ => unreachable!(),
}
return Ok(());
}

View file

@ -755,9 +755,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
dereferenced_const.print(self)?;
}
ty::Array(..) | ty::Tuple(..) | ty::Adt(..) | ty::Slice(_) => {
let contents = self.tcx.destructure_const(ct);
let fields = contents.fields.iter().copied();
ty::Array(..) | ty::Tuple(..) | ty::Slice(_) => {
let fields = cv.to_branch().iter().copied();
let print_field_list = |this: &mut Self| {
for field in fields.clone() {
@ -776,45 +775,53 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
self.push("T");
print_field_list(self)?;
}
ty::Adt(def, args) => {
let variant_idx =
contents.variant.expect("destructed const of adt without variant idx");
let variant_def = &def.variant(variant_idx);
self.push("V");
self.print_def_path(variant_def.def_id, args)?;
match variant_def.ctor_kind() {
Some(CtorKind::Const) => {
self.push("U");
}
Some(CtorKind::Fn) => {
self.push("T");
print_field_list(self)?;
}
None => {
self.push("S");
for (field_def, field) in iter::zip(&variant_def.fields, fields) {
// HACK(eddyb) this mimics `print_path_with_simple`,
// instead of simply using `field_def.ident`,
// just to be able to handle disambiguators.
let disambiguated_field =
self.tcx.def_key(field_def.did).disambiguated_data;
let field_name = disambiguated_field.data.get_opt_name();
self.push_disambiguator(
disambiguated_field.disambiguator as u64,
);
self.push_ident(field_name.unwrap().as_str());
field.print(self)?;
}
self.push("E");
}
}
}
_ => unreachable!(),
}
}
ty::Adt(def, args) => {
let contents = cv.destructure_adt_const();
let fields = contents.fields.iter().copied();
let print_field_list = |this: &mut Self| {
for field in fields.clone() {
field.print(this)?;
}
this.push("E");
Ok(())
};
let variant_idx = contents.variant;
let variant_def = &def.variant(variant_idx);
self.push("V");
self.print_def_path(variant_def.def_id, args)?;
match variant_def.ctor_kind() {
Some(CtorKind::Const) => {
self.push("U");
}
Some(CtorKind::Fn) => {
self.push("T");
print_field_list(self)?;
}
None => {
self.push("S");
for (field_def, field) in iter::zip(&variant_def.fields, fields) {
// HACK(eddyb) this mimics `print_path_with_simple`,
// instead of simply using `field_def.ident`,
// just to be able to handle disambiguators.
let disambiguated_field =
self.tcx.def_key(field_def.did).disambiguated_data;
let field_name = disambiguated_field.data.get_opt_name();
self.push_disambiguator(disambiguated_field.disambiguator as u64);
self.push_ident(field_name.unwrap().as_str());
field.print(self)?;
}
self.push("E");
}
}
}
_ => {
bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct_ty, ct);
}

View file

@ -1,6 +1,3 @@
use std::iter;
use rustc_abi::{FIRST_VARIANT, VariantIdx};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
@ -10,63 +7,12 @@ use rustc_middle::thir::visit;
use rustc_middle::thir::visit::Visitor;
use rustc_middle::ty::abstract_const::CastKind;
use rustc_middle::ty::{self, Expr, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, mir, thir};
use rustc_middle::{mir, thir};
use rustc_span::Span;
use tracing::{debug, instrument};
use tracing::instrument;
use crate::errors::{GenericConstantTooComplex, GenericConstantTooComplexSub};
/// Destructures array, ADT or tuple constants into the constants
/// of their fields.
fn destructure_const<'tcx>(
tcx: TyCtxt<'tcx>,
const_: ty::Const<'tcx>,
) -> ty::DestructuredConst<'tcx> {
let ty::ConstKind::Value(cv) = const_.kind() else {
bug!("cannot destructure constant {:?}", const_)
};
let branches = cv.to_branch();
let (fields, variant) = match cv.ty.kind() {
ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
// construct the consts for the elements of the array/slice
let field_consts = branches
.iter()
.map(|b| ty::Const::new_value(tcx, b.to_value().valtree, *inner_ty))
.collect::<Vec<_>>();
debug!(?field_consts);
(field_consts, None)
}
ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"),
ty::Adt(def, _) => {
let (variant_idx, field_consts) = if def.is_enum() {
let (head, rest) = branches.split_first().unwrap();
(VariantIdx::from_u32(head.to_leaf().to_u32()), rest)
} else {
(FIRST_VARIANT, branches)
};
debug!(?field_consts);
(field_consts.to_vec(), Some(variant_idx))
}
ty::Tuple(elem_tys) => {
let fields = iter::zip(*elem_tys, branches)
.map(|(elem_ty, elem_valtree)| {
ty::Const::new_value(tcx, elem_valtree.to_value().valtree, elem_ty)
})
.collect::<Vec<_>>();
(fields, None)
}
_ => bug!("cannot destructure constant {:?}", const_),
};
let fields = tcx.arena.alloc_from_iter(fields);
ty::DestructuredConst { variant, fields }
}
/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
fn check_binop(op: mir::BinOp) -> bool {
use mir::BinOp::*;
@ -432,5 +378,5 @@ fn thir_abstract_const<'tcx>(
}
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { destructure_const, thir_abstract_const, ..*providers };
*providers = Providers { thir_abstract_const, ..*providers };
}

View file

@ -1,8 +0,0 @@
//@ known-bug: #131052
#![feature(adt_const_params)]
struct ConstBytes<const T: &'static [*mut u8; 3]>;
pub fn main() {
let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
}

View file

@ -0,0 +1,13 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/131052>
#![feature(adt_const_params)]
struct ConstBytes<const T: &'static [*mut u8; 3]>;
//~^ ERROR `&'static [*mut u8; 3]` can't be used as a const parameter type
pub fn main() {
let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
//~^ ERROR mismatched types
//~| ERROR mismatched types
//~| ERROR mismatched types
}

View file

@ -0,0 +1,42 @@
error[E0741]: `&'static [*mut u8; 3]` can't be used as a const parameter type
--> $DIR/mismatch-raw-ptr-in-adt.rs:5:28
|
LL | struct ConstBytes<const T: &'static [*mut u8; 3]>;
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: `*mut u8` must implement `ConstParamTy_`, but it does not
= note: `[*mut u8; 3]` must implement `ConstParamTy_`, but it does not
error[E0308]: mismatched types
--> $DIR/mismatch-raw-ptr-in-adt.rs:9:33
|
LL | let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
| ------------------ ^^^^^^^^^^^^^^^^^^^^ expected `&[65, 65, 65]`, found `&[66, 66, 66]`
| |
| expected due to this
|
= note: expected struct `ConstBytes<&[65, 65, 65]>`
found struct `ConstBytes<&[66, 66, 66]>`
error[E0308]: mismatched types
--> $DIR/mismatch-raw-ptr-in-adt.rs:9:46
|
LL | let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
| ^^^^^^ expected `&[*mut u8; 3]`, found `&[u8; 3]`
|
= note: expected reference `&'static [*mut u8; 3]`
found reference `&'static [u8; 3]`
error[E0308]: mismatched types
--> $DIR/mismatch-raw-ptr-in-adt.rs:9:23
|
LL | let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
| ^^^^^^ expected `&[*mut u8; 3]`, found `&[u8; 3]`
|
= note: expected reference `&'static [*mut u8; 3]`
found reference `&'static [u8; 3]`
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0308, E0741.
For more information about an error, try `rustc --explain E0308`.