From cd88bb332c40deda6db2c18c0bc03cfd0da42055 Mon Sep 17 00:00:00 2001 From: Dominik Stolz Date: Tue, 28 Jun 2022 22:35:48 +0200 Subject: [PATCH 1/4] Improve pretty printing of valtrees for references --- .../rustc_const_eval/src/const_eval/mod.rs | 78 ----------------- compiler/rustc_const_eval/src/lib.rs | 1 - .../rustc_middle/src/mir/interpret/queries.rs | 8 +- compiler/rustc_middle/src/query/mod.rs | 7 +- compiler/rustc_middle/src/ty/print/pretty.rs | 18 ++-- compiler/rustc_ty_utils/src/consts.rs | 84 +++++++++++++++++++ compiler/rustc_ty_utils/src/lib.rs | 2 + src/test/ui/const-generics/issue-66451.rs | 28 +++++++ src/test/ui/const-generics/issue-66451.stderr | 20 +++++ 9 files changed, 144 insertions(+), 102 deletions(-) create mode 100644 compiler/rustc_ty_utils/src/consts.rs create mode 100644 src/test/ui/const-generics/issue-66451.rs create mode 100644 src/test/ui/const-generics/issue-66451.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index a1d2e5cf3ef12..bf65fdc54ca48 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -5,7 +5,6 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; -use rustc_target::abi::VariantIdx; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta, @@ -91,83 +90,6 @@ pub(crate) fn eval_to_valtree<'tcx>( } } -/// Tries to destructure constants of type Array or Adt into the constants -/// of its fields. -pub(crate) fn try_destructure_const<'tcx>( - tcx: TyCtxt<'tcx>, - const_: ty::Const<'tcx>, -) -> Option> { - if let ty::ConstKind::Value(valtree) = const_.kind() { - let branches = match valtree { - ty::ValTree::Branch(b) => b, - _ => return None, - }; - - let (fields, variant) = match const_.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| { - tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty }) - }) - .collect::>(); - debug!(?field_consts); - - (field_consts, None) - } - ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"), - ty::Adt(def, substs) => { - let variant_idx = if def.is_enum() { - VariantIdx::from_u32(branches[0].unwrap_leaf().try_to_u32().ok()?) - } else { - VariantIdx::from_u32(0) - }; - let fields = &def.variant(variant_idx).fields; - let mut field_consts = Vec::with_capacity(fields.len()); - - // Note: First element inValTree corresponds to variant of enum - let mut valtree_idx = if def.is_enum() { 1 } else { 0 }; - for field in fields { - let field_ty = field.ty(tcx, substs); - let field_valtree = branches[valtree_idx]; // first element of branches is variant - let field_const = tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Value(field_valtree), - ty: field_ty, - }); - field_consts.push(field_const); - valtree_idx += 1; - } - debug!(?field_consts); - - (field_consts, Some(variant_idx)) - } - ty::Tuple(elem_tys) => { - let fields = elem_tys - .iter() - .enumerate() - .map(|(i, elem_ty)| { - let elem_valtree = branches[i]; - tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Value(elem_valtree), - ty: elem_ty, - }) - }) - .collect::>(); - - (fields, None) - } - _ => bug!("cannot destructure constant {:?}", const_), - }; - - let fields = tcx.arena.alloc_from_iter(fields.into_iter()); - - Some(ty::DestructuredConst { variant, fields }) - } else { - None - } -} - #[instrument(skip(tcx), level = "debug")] pub(crate) fn try_destructure_mir_constant<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 64a74e9d7e206..5bf91879066f4 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -42,7 +42,6 @@ pub fn provide(providers: &mut Providers) { providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.const_caller_location = const_eval::const_caller_location; - providers.try_destructure_const = |tcx, val| const_eval::try_destructure_const(tcx, val); providers.eval_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); const_eval::eval_to_valtree(tcx, param_env, raw) diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 4895b53bb2677..575147feebc94 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -205,14 +205,8 @@ impl<'tcx> TyCtxtEnsure<'tcx> { } impl<'tcx> TyCtxt<'tcx> { - /// Destructure a type-level constant ADT or array into its variant index and its field values. - /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. - pub fn destructure_const(self, const_: ty::Const<'tcx>) -> ty::DestructuredConst<'tcx> { - self.try_destructure_const(const_).unwrap() - } - /// Destructure a mir constant ADT or array into its variant index and its field values. - /// Panics if the destructuring fails, use `try_destructure_const` for fallible version. + /// Panics if the destructuring fails, use `try_destructure_mir_constant` for fallible version. pub fn destructure_mir_constant( self, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 2e68fc8a7c050..b07916d3bbc5a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -978,11 +978,8 @@ rustc_queries! { desc { "converting type-level constant value to mir constant value"} } - /// Destructure a constant ADT or array into its variant index and its - /// field values or return `None` if constant is invalid. - /// - /// Use infallible `TyCtxt::destructure_const` when you know that constant is valid. - query try_destructure_const(key: ty::Const<'tcx>) -> Option> { + /// Destructure a type-level constant ADT or array into its variant index and its field values. + query destructure_const(key: ty::Const<'tcx>) -> ty::DestructuredConst<'tcx> { desc { "destructuring type level constant"} } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c56909ba18b14..7f3b0fdccc6f1 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1447,7 +1447,11 @@ pub trait PrettyPrinter<'tcx>: p!(write("{:?}", String::from_utf8_lossy(bytes))); return Ok(self); } - _ => {} + _ => { + p!("&"); + p!(pretty_print_const_valtree(valtree, *inner_ty, print_ty)); + return Ok(self); + } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| { @@ -1459,16 +1463,8 @@ pub trait PrettyPrinter<'tcx>: } // Aggregates, printed as array/tuple/struct/variant construction syntax. (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { - let Some(contents) = self.tcx().try_destructure_const( - ty::Const::from_value(self.tcx(), valtree, ty) - ) else { - // Fall back to debug pretty printing for invalid constants. - p!(write("{:?}", valtree)); - if print_ty { - p!(": ", print(ty)); - } - return Ok(self); - }; + let contents = + self.tcx().destructure_const(ty::Const::from_value(self.tcx(), valtree, ty)); let fields = contents.fields.iter().copied(); match *ty.kind() { ty::Array(..) => { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs new file mode 100644 index 0000000000000..22cfd7e19853e --- /dev/null +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -0,0 +1,84 @@ +use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::VariantIdx; + +/// Tries to destructure constants of type Array or Adt into the constants +/// of its fields. +pub(crate) fn destructure_const<'tcx>( + tcx: TyCtxt<'tcx>, + const_: ty::Const<'tcx>, +) -> ty::DestructuredConst<'tcx> { + if let ty::ConstKind::Value(valtree) = const_.kind() { + let branches = match valtree { + ty::ValTree::Branch(b) => b, + _ => bug!("cannot destructure constant {:?}", const_), + }; + + let (fields, variant) = match const_.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| { + tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty }) + }) + .collect::>(); + debug!(?field_consts); + + (field_consts, None) + } + ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"), + ty::Adt(def, substs) => { + let variant_idx = if def.is_enum() { + VariantIdx::from_u32(branches[0].unwrap_leaf().try_to_u32().unwrap()) + } else { + VariantIdx::from_u32(0) + }; + let fields = &def.variant(variant_idx).fields; + let mut field_consts = Vec::with_capacity(fields.len()); + + // Note: First element inValTree corresponds to variant of enum + let mut valtree_idx = if def.is_enum() { 1 } else { 0 }; + for field in fields { + let field_ty = field.ty(tcx, substs); + let field_valtree = branches[valtree_idx]; // first element of branches is variant + let field_const = tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Value(field_valtree), + ty: field_ty, + }); + field_consts.push(field_const); + valtree_idx += 1; + } + debug!(?field_consts); + + (field_consts, Some(variant_idx)) + } + ty::Tuple(elem_tys) => { + let fields = elem_tys + .iter() + .enumerate() + .map(|(i, elem_ty)| { + let elem_valtree = branches[i]; + tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Value(elem_valtree), + ty: elem_ty, + }) + }) + .collect::>(); + + (fields, None) + } + _ => bug!("cannot destructure constant {:?}", const_), + }; + + let fields = tcx.arena.alloc_from_iter(fields.into_iter()); + + ty::DestructuredConst { variant, fields } + } else { + bug!("cannot destructure constant {:?}", const_) + } +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = + ty::query::Providers { destructure_const, ..*providers }; +} diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 484967bbef8ce..a27cb7cf07e72 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::query::Providers; mod assoc; mod common_traits; +pub mod consts; pub mod instance; mod needs_drop; pub mod representability; @@ -26,6 +27,7 @@ mod ty; pub fn provide(providers: &mut Providers) { assoc::provide(providers); common_traits::provide(providers); + consts::provide(providers); needs_drop::provide(providers); ty::provide(providers); instance::provide(providers); diff --git a/src/test/ui/const-generics/issue-66451.rs b/src/test/ui/const-generics/issue-66451.rs new file mode 100644 index 0000000000000..76505d1887594 --- /dev/null +++ b/src/test/ui/const-generics/issue-66451.rs @@ -0,0 +1,28 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +#[derive(Debug, PartialEq, Eq)] +struct Foo { + value: i32, + nested: &'static Bar, +} + +#[derive(Debug, PartialEq, Eq)] +struct Bar(T); + +struct Test; + +fn main() { + let x: Test<{ + Foo { + value: 3, + nested: &Bar(4), + } + }> = Test; + let y: Test<{ + Foo { + value: 3, + nested: &Bar(5), + } + }> = x; //~ ERROR mismatched types +} diff --git a/src/test/ui/const-generics/issue-66451.stderr b/src/test/ui/const-generics/issue-66451.stderr new file mode 100644 index 0000000000000..b691eac4f2d0e --- /dev/null +++ b/src/test/ui/const-generics/issue-66451.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/issue-66451.rs:27:10 + | +LL | let y: Test<{ + | ____________- +LL | | Foo { +LL | | value: 3, +LL | | nested: &Bar(5), +LL | | } +LL | | }> = x; + | | - ^ expected `Foo { value: 3_i32, nested: &Bar::(5_i32) }`, found `Foo { value: 3_i32, nested: &Bar::(4_i32) }` + | |______| + | expected due to this + | + = note: expected struct `Test(5_i32) }>` + found struct `Test(4_i32) }>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 080525229b9830c727cf447236deb7bdae7d5814 Mon Sep 17 00:00:00 2001 From: Dominik Stolz Date: Tue, 28 Jun 2022 22:45:05 +0200 Subject: [PATCH 2/4] Make consts mod private --- compiler/rustc_ty_utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index a27cb7cf07e72..7624d31b40bc7 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::query::Providers; mod assoc; mod common_traits; -pub mod consts; +mod consts; pub mod instance; mod needs_drop; pub mod representability; From 053f48d91f3529a3aa1c82a1e96e9b77ef7edf30 Mon Sep 17 00:00:00 2001 From: Dominik Stolz Date: Tue, 28 Jun 2022 23:26:54 +0200 Subject: [PATCH 3/4] Address code review comments --- compiler/rustc_ty_utils/src/consts.rs | 119 ++++++++++------------ src/test/ui/const-generics/issue-66451.rs | 2 +- 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 22cfd7e19853e..5f7cafe2722fd 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -1,84 +1,77 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_target::abi::VariantIdx; -/// Tries to destructure constants of type Array or Adt into the constants +use std::iter; + +/// Tries to destructure array, ADT or tuple constants into the constants /// of its fields. pub(crate) fn destructure_const<'tcx>( tcx: TyCtxt<'tcx>, const_: ty::Const<'tcx>, ) -> ty::DestructuredConst<'tcx> { - if let ty::ConstKind::Value(valtree) = const_.kind() { - let branches = match valtree { - ty::ValTree::Branch(b) => b, - _ => bug!("cannot destructure constant {:?}", const_), - }; + let ty::ConstKind::Value(valtree) = const_.kind() else { + bug!("cannot destructure constant {:?}", const_) + }; - let (fields, variant) = match const_.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| { - tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty }) - }) - .collect::>(); - debug!(?field_consts); + let branches = match valtree { + ty::ValTree::Branch(b) => b, + _ => bug!("cannot destructure constant {:?}", const_), + }; - (field_consts, None) - } - ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"), - ty::Adt(def, substs) => { - let variant_idx = if def.is_enum() { - VariantIdx::from_u32(branches[0].unwrap_leaf().try_to_u32().unwrap()) - } else { - VariantIdx::from_u32(0) - }; - let fields = &def.variant(variant_idx).fields; - let mut field_consts = Vec::with_capacity(fields.len()); + let (fields, variant) = match const_.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| tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty })) + .collect::>(); + debug!(?field_consts); - // Note: First element inValTree corresponds to variant of enum - let mut valtree_idx = if def.is_enum() { 1 } else { 0 }; - for field in fields { - let field_ty = field.ty(tcx, substs); - let field_valtree = branches[valtree_idx]; // first element of branches is variant - let field_const = tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Value(field_valtree), - ty: field_ty, - }); - field_consts.push(field_const); - valtree_idx += 1; - } - debug!(?field_consts); + (field_consts, None) + } + ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"), + ty::Adt(def, substs) => { + let (variant_idx, branches) = if def.is_enum() { + let (head, rest) = branches.split_first().unwrap(); + (VariantIdx::from_u32(head.unwrap_leaf().try_to_u32().unwrap()), rest) + } else { + (VariantIdx::from_u32(0), branches) + }; + let fields = &def.variant(variant_idx).fields; + let mut field_consts = Vec::with_capacity(fields.len()); - (field_consts, Some(variant_idx)) + for (field, field_valtree) in iter::zip(fields, branches) { + let field_ty = field.ty(tcx, substs); + let field_const = tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Value(*field_valtree), + ty: field_ty, + }); + field_consts.push(field_const); } - ty::Tuple(elem_tys) => { - let fields = elem_tys - .iter() - .enumerate() - .map(|(i, elem_ty)| { - let elem_valtree = branches[i]; - tcx.mk_const(ty::ConstS { - kind: ty::ConstKind::Value(elem_valtree), - ty: elem_ty, - }) + debug!(?field_consts); + + (field_consts, Some(variant_idx)) + } + ty::Tuple(elem_tys) => { + let fields = iter::zip(*elem_tys, branches) + .map(|(elem_ty, elem_valtree)| { + tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Value(*elem_valtree), + ty: elem_ty, }) - .collect::>(); + }) + .collect::>(); - (fields, None) - } - _ => bug!("cannot destructure constant {:?}", const_), - }; + (fields, None) + } + _ => bug!("cannot destructure constant {:?}", const_), + }; - let fields = tcx.arena.alloc_from_iter(fields.into_iter()); + let fields = tcx.arena.alloc_from_iter(fields.into_iter()); - ty::DestructuredConst { variant, fields } - } else { - bug!("cannot destructure constant {:?}", const_) - } + ty::DestructuredConst { variant, fields } } pub fn provide(providers: &mut ty::query::Providers) { - *providers = - ty::query::Providers { destructure_const, ..*providers }; + *providers = ty::query::Providers { destructure_const, ..*providers }; } diff --git a/src/test/ui/const-generics/issue-66451.rs b/src/test/ui/const-generics/issue-66451.rs index 76505d1887594..3335f7d598480 100644 --- a/src/test/ui/const-generics/issue-66451.rs +++ b/src/test/ui/const-generics/issue-66451.rs @@ -17,7 +17,7 @@ fn main() { Foo { value: 3, nested: &Bar(4), - } + } }> = Test; let y: Test<{ Foo { From d048b15216a14711b386ac5322cf24583df5ed3d Mon Sep 17 00:00:00 2001 From: Dominik Stolz Date: Wed, 29 Jun 2022 10:30:47 +0200 Subject: [PATCH 4/4] Improve doc comment of destructure_const --- compiler/rustc_middle/src/query/mod.rs | 3 ++- compiler/rustc_ty_utils/src/consts.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b07916d3bbc5a..a1065eef8509d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -978,7 +978,8 @@ rustc_queries! { desc { "converting type-level constant value to mir constant value"} } - /// Destructure a type-level constant ADT or array into its variant index and its field values. + /// 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"} } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 5f7cafe2722fd..0b83cdb78dce7 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -3,8 +3,8 @@ use rustc_target::abi::VariantIdx; use std::iter; -/// Tries to destructure array, ADT or tuple constants into the constants -/// of its fields. +/// Destructures array, ADT or tuple constants into the constants +/// of their fields. pub(crate) fn destructure_const<'tcx>( tcx: TyCtxt<'tcx>, const_: ty::Const<'tcx>,