diff options
| author | b-naber <bn263@gmx.de> | 2022-02-16 10:56:01 +0100 |
|---|---|---|
| committer | b-naber <bn263@gmx.de> | 2022-06-14 16:07:11 +0200 |
| commit | 705d818bd52a6324d5e7693cc4306457395eebc8 (patch) | |
| tree | f5363e1a8b0426bc961970028c23670869e344fb /compiler/rustc_middle/src | |
| parent | edab34ab2abbafc16a78daedf71dbacd2eb0b7bf (diff) | |
| download | rust-705d818bd52a6324d5e7693cc4306457395eebc8.tar.gz rust-705d818bd52a6324d5e7693cc4306457395eebc8.zip | |
implement valtrees as the type-system representation for constant values
Diffstat (limited to 'compiler/rustc_middle/src')
| -rw-r--r-- | compiler/rustc_middle/src/mir/interpret/queries.rs | 80 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/interpret/value.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/mod.rs | 269 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/pretty.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/query.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/mir/terminator.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/query/mod.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/traits/chalk.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/consts.rs | 35 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/consts/kind.rs | 83 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/consts/valtree.rs | 59 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/context.rs | 68 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/instance.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/mod.rs | 32 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/print/mod.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/print/pretty.rs | 123 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/relate.rs | 65 |
17 files changed, 600 insertions, 260 deletions
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 8fc957cf49c..69353232f06 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,4 +1,4 @@ -use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; +use super::{ErrorHandled, EvalToConstValueResult, EvalToValTreeResult, GlobalId}; use crate::mir; use crate::ty::fold::TypeFoldable; @@ -11,6 +11,10 @@ impl<'tcx> TyCtxt<'tcx> { /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts /// that can't take any generic arguments like statics, const items or enum discriminants. If a /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. + /// + /// Note: Returns a `ConstValue`, which isn't supposed to be used in the type system. In order to + /// evaluate to a type-system level constant value use `const_eval_poly_for_typeck`. + #[instrument(skip(self), level = "debug")] pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> { // In some situations def_id will have substitutions within scope, but they aren't allowed // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions @@ -23,6 +27,22 @@ impl<'tcx> TyCtxt<'tcx> { self.const_eval_global_id(param_env, cid, None) } + /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts + /// that can't take any generic arguments like statics, const items or enum discriminants. If a + /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. + #[instrument(skip(self), level = "debug")] + pub fn const_eval_poly_for_typeck(self, def_id: DefId) -> EvalToValTreeResult<'tcx> { + // In some situations def_id will have substitutions within scope, but they aren't allowed + // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions + // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are + // encountered. + let substs = InternalSubsts::identity_for_item(self, def_id); + let instance = ty::Instance::new(def_id, substs); + let cid = GlobalId { instance, promoted: None }; + let param_env = self.param_env(def_id).with_reveal_all_normalized(self); + self.const_eval_global_id_for_typeck(param_env, cid, None) + } + /// Resolves and evaluates a constant. /// /// The constant can be located on a trait like `<A as B>::C`, in which case the given @@ -59,6 +79,33 @@ impl<'tcx> TyCtxt<'tcx> { } } + #[instrument(level = "debug", skip(self))] + pub fn const_eval_resolve_for_typeck( + self, + param_env: ty::ParamEnv<'tcx>, + ct: ty::Unevaluated<'tcx>, + span: Option<Span>, + ) -> EvalToValTreeResult<'tcx> { + // Cannot resolve `Unevaluated` constants that contain inference + // variables. We reject those here since `resolve_opt_const_arg` + // would fail otherwise. + // + // When trying to evaluate constants containing inference variables, + // use `Infcx::const_eval_resolve` instead. + if ct.substs.has_infer_types_or_consts() { + bug!("did not expect inference variables here"); + } + + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { + Ok(Some(instance)) => { + let cid = GlobalId { instance, promoted: ct.promoted }; + self.const_eval_global_id_for_typeck(param_env, cid, span) + } + Ok(None) => Err(ErrorHandled::TooGeneric), + Err(error_reported) => Err(ErrorHandled::Reported(error_reported)), + } + } + pub fn const_eval_instance( self, param_env: ty::ParamEnv<'tcx>, @@ -68,7 +115,8 @@ impl<'tcx> TyCtxt<'tcx> { self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) } - /// Evaluate a constant. + /// Evaluate a constant to a `ConstValue`. + #[instrument(skip(self), level = "debug")] pub fn const_eval_global_id( self, param_env: ty::ParamEnv<'tcx>, @@ -86,6 +134,27 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Evaluate a constant to a type-level constant. + #[instrument(skip(self), level = "debug")] + pub fn const_eval_global_id_for_typeck( + self, + param_env: ty::ParamEnv<'tcx>, + cid: GlobalId<'tcx>, + span: Option<Span>, + ) -> EvalToValTreeResult<'tcx> { + let param_env = param_env.with_const(); + debug!(?param_env); + // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should + // improve caching of queries. + let inputs = self.erase_regions(param_env.and(cid)); + debug!(?inputs); + if let Some(span) = span { + self.at(span).eval_to_valtree(inputs) + } else { + self.eval_to_valtree(inputs) + } + } + /// Evaluate a static's initializer, returning the allocation of the initializer's memory. #[inline(always)] pub fn eval_static_initializer( @@ -125,11 +194,8 @@ impl<'tcx> TyCtxtAt<'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, - param_env_and_val: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>, - ) -> mir::DestructuredConst<'tcx> { - self.try_destructure_const(param_env_and_val).unwrap() + pub fn destructure_const(self, const_: ty::Const<'tcx>) -> mir::DestructuredConst<'tcx> { + self.try_destructure_const(const_).unwrap() } /// Destructure a mir constant ADT or array into its variant index and its field values. diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 146ae45e468..f566467b7ce 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -78,6 +78,7 @@ impl<'tcx> ConstValue<'tcx> { Some(self.try_to_scalar()?.assert_int()) } + #[inline(always)] pub fn try_to_bits(&self, size: Size) -> Option<u128> { self.try_to_scalar_int()?.to_bits(size).ok() } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 1511b51fa25..512615ccbab 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3,7 +3,9 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html use crate::mir::coverage::{CodeRegion, CoverageKind}; -use crate::mir::interpret::{ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar}; +use crate::mir::interpret::{ + AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar, +}; use crate::mir::visit::MirVisitable; use crate::ty::adjustment::PointerCast; use crate::ty::codec::{TyDecoder, TyEncoder}; @@ -1444,7 +1446,11 @@ impl<'tcx> BasicBlockData<'tcx> { } pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { - if index < self.statements.len() { &self.statements[index] } else { &self.terminator } + if index < self.statements.len() { + &self.statements[index] + } else { + &self.terminator + } } } @@ -2465,7 +2471,11 @@ impl<'tcx> Operand<'tcx> { /// find as the `func` in a [`TerminatorKind::Call`]. pub fn const_fn_def(&self) -> Option<(DefId, SubstsRef<'tcx>)> { let const_ty = self.constant()?.literal.ty(); - if let ty::FnDef(def_id, substs) = *const_ty.kind() { Some((def_id, substs)) } else { None } + if let ty::FnDef(def_id, substs) = *const_ty.kind() { + Some((def_id, substs)) + } else { + None + } } } @@ -2953,22 +2963,9 @@ impl<'tcx> Constant<'tcx> { } } -impl<'tcx> From<ty::Const<'tcx>> for ConstantKind<'tcx> { - #[inline] - fn from(ct: ty::Const<'tcx>) -> Self { - match ct.kind() { - ty::ConstKind::Value(cv) => { - // FIXME Once valtrees are introduced we need to convert those - // into `ConstValue` instances here - Self::Val(cv, ct.ty()) - } - _ => Self::Ty(ct), - } - } -} - impl<'tcx> ConstantKind<'tcx> { /// Returns `None` if the constant is not trivially safe for use in the type system. + #[inline] pub fn const_for_ty(&self) -> Option<ty::Const<'tcx>> { match self { ConstantKind::Ty(c) => Some(*c), @@ -2976,6 +2973,7 @@ impl<'tcx> ConstantKind<'tcx> { } } + #[inline(always)] pub fn ty(&self) -> Ty<'tcx> { match self { ConstantKind::Ty(c) => c.ty(), @@ -2983,10 +2981,10 @@ impl<'tcx> ConstantKind<'tcx> { } } - pub fn try_val(&self) -> Option<ConstValue<'tcx>> { + pub fn try_val(&self, tcx: TyCtxt<'tcx>) -> Option<ConstValue<'tcx>> { match self { ConstantKind::Ty(c) => match c.kind() { - ty::ConstKind::Value(v) => Some(v), + ty::ConstKind::Value(v) => Some(tcx.valtree_to_const_val((c.ty(), v))), _ => None, }, ConstantKind::Val(v, _) => Some(*v), @@ -2994,21 +2992,33 @@ impl<'tcx> ConstantKind<'tcx> { } #[inline] - pub fn try_to_value(self) -> Option<interpret::ConstValue<'tcx>> { + pub fn try_to_value(self, tcx: TyCtxt<'tcx>) -> Option<interpret::ConstValue<'tcx>> { match self { - ConstantKind::Ty(c) => c.kind().try_to_value(), + ConstantKind::Ty(c) => match c.kind() { + ty::ConstKind::Value(valtree) => Some(tcx.valtree_to_const_val((c.ty(), valtree))), + _ => None, + }, ConstantKind::Val(val, _) => Some(val), } } #[inline] pub fn try_to_scalar(self) -> Option<Scalar> { - self.try_to_value()?.try_to_scalar() + match self { + ConstantKind::Ty(c) => match c.val() { + ty::ConstKind::Value(valtree) => match valtree { + ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)), + ty::ValTree::Branch(_) => None, + }, + _ => None, + }, + ConstantKind::Val(val, _) => val.try_to_scalar(), + } } #[inline] pub fn try_to_scalar_int(self) -> Option<ScalarInt> { - Some(self.try_to_value()?.try_to_scalar()?.assert_int()) + Some(self.try_to_scalar()?.assert_int()) } #[inline] @@ -3025,9 +3035,7 @@ impl<'tcx> ConstantKind<'tcx> { pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { match self { Self::Ty(c) => { - // FIXME Need to use a different evaluation function that directly returns a `ConstValue` - // if evaluation succeeds and does not create a ValTree first - if let Some(val) = c.kind().try_eval(tcx, param_env) { + if let Some(val) = c.kind().try_eval_for_mir(tcx, param_env) { match val { Ok(val) => Self::Val(val, c.ty()), Err(_) => Self::Ty(tcx.const_error(self.ty())), @@ -3081,6 +3089,11 @@ impl<'tcx> ConstantKind<'tcx> { } } + #[inline] + pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self { + Self::Val(val, ty) + } + pub fn from_bits( tcx: TyCtxt<'tcx>, bits: u128, @@ -3097,11 +3110,13 @@ impl<'tcx> ConstantKind<'tcx> { Self::Val(cv, param_env_ty.value) } + #[inline] pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self { let cv = ConstValue::from_bool(v); Self::Val(cv, tcx.types.bool) } + #[inline] pub fn zero_sized(ty: Ty<'tcx>) -> Self { let cv = ConstValue::Scalar(Scalar::ZST); Self::Val(cv, ty) @@ -3112,6 +3127,12 @@ impl<'tcx> ConstantKind<'tcx> { Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty)) } + #[inline] + pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self { + let val = ConstValue::Scalar(s); + Self::Val(val, ty) + } + /// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly /// converted to a constant, everything else becomes `Unevaluated`. pub fn from_anon_const( @@ -3199,8 +3220,10 @@ impl<'tcx> ConstantKind<'tcx> { } _ => expr, }; + debug!("expr.kind: {:?}", expr.kind); let ty = tcx.type_of(def.def_id_for_type_of()); + debug!(?ty); // FIXME(const_generics): We currently have to special case parameters because `min_const_generics` // does not provide the parents generics to anonymous constants. We still allow generic const @@ -3224,6 +3247,7 @@ impl<'tcx> ConstantKind<'tcx> { kind: ty::ConstKind::Param(ty::ParamConst::new(index, name)), ty, }); + debug!(?ty_const); return Self::Ty(ty_const); } @@ -3253,8 +3277,12 @@ impl<'tcx> ConstantKind<'tcx> { debug!(?span, ?param_env); match tcx.const_eval_resolve(param_env, uneval, Some(span)) { - Ok(val) => Self::Val(val, ty), + Ok(val) => { + debug!("evaluated const value: {:?}", val); + Self::Val(val, ty) + } Err(_) => { + debug!("error encountered during evaluation"); // Error was handled in `const_eval_resolve`. Here we just create a // new unevaluated const and error hard later in codegen let ty_const = tcx.mk_const(ty::ConstS { @@ -3265,11 +3293,22 @@ impl<'tcx> ConstantKind<'tcx> { }), ty, }); + debug!(?ty_const); Self::Ty(ty_const) } } } + + pub fn from_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + match c.val() { + ty::ConstKind::Value(valtree) => { + let const_val = tcx.valtree_to_const_val((c.ty(), valtree)); + Self::Val(const_val, c.ty()) + } + _ => Self::Ty(c), + } + } } /// A collection of projections into user types. @@ -3485,20 +3524,182 @@ fn pretty_print_const<'tcx>( }) } +fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result { + fmt.write_str("b\"")?; + for &c in byte_str { + for e in std::ascii::escape_default(c) { + fmt.write_char(e as char)?; + } + } + fmt.write_str("\"")?; + + Ok(()) +} + +fn comma_sep<'tcx>(fmt: &mut Formatter<'_>, elems: Vec<ConstantKind<'tcx>>) -> fmt::Result { + let mut first = true; + for elem in elems { + if !first { + fmt.write_str(", ")?; + } + fmt.write_str(&format!("{}", elem))?; + first = false; + } + Ok(()) +} + fn pretty_print_const_value<'tcx>( - val: interpret::ConstValue<'tcx>, + ct: ConstValue<'tcx>, ty: Ty<'tcx>, fmt: &mut Formatter<'_>, - print_types: bool, + print_ty: bool, ) -> fmt::Result { use crate::ty::print::PrettyPrinter; + ty::tls::with(|tcx| { - let val = tcx.lift(val).unwrap(); + let ct = tcx.lift(ct).unwrap(); let ty = tcx.lift(ty).unwrap(); - let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.print_alloc_ids = true; - let cx = cx.pretty_print_const_value(val, ty, print_types)?; - fmt.write_str(&cx.into_buffer())?; + + if tcx.sess.verbose() { + fmt.write_str(&format!("ConstValue({:?}: {})", ct, ty))?; + return Ok(()); + } + + let u8_type = tcx.types.u8; + match (ct, ty.kind()) { + // Byte/string slices, printed as (byte) string literals. + (ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => { + match inner.kind() { + ty::Slice(t) => { + if *t == u8_type { + // The `inspect` here is okay since we checked the bounds, and there are + // no relocations (we have an active slice reference here). We don't use + // this result to affect interpreter execution. + let byte_str = data + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(start..end); + pretty_print_byte_str(fmt, byte_str)?; + return Ok(()); + } + } + ty::Str => { + // The `inspect` here is okay since we checked the bounds, and there are no + // relocations (we have an active `str` reference here). We don't use this + // result to affect interpreter execution. + let slice = data + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(start..end); + fmt.write_str(&format!("{:?}", String::from_utf8_lossy(slice)))?; + return Ok(()); + } + _ => {} + } + } + (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { + let n = n.val().try_to_bits(tcx.data_layout.pointer_size).unwrap(); + // cast is ok because we already checked for pointer size (32 or 64 bit) above + let range = AllocRange { start: offset, size: Size::from_bytes(n) }; + let byte_str = alloc.inner().get_bytes(&tcx, range).unwrap(); + fmt.write_str("*")?; + pretty_print_byte_str(fmt, byte_str)?; + return Ok(()); + } + // Aggregates, printed as array/tuple/struct/variant construction syntax. + // + // NB: the `has_param_types_or_consts` check ensures that we can use + // the `destructure_const` query with an empty `ty::ParamEnv` 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) + // + // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the + // correct `ty::ParamEnv` to allow printing *all* constant values. + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + let ct = tcx.lift(ct).unwrap(); + let ty = tcx.lift(ty).unwrap(); + if let Some(contents) = tcx.try_destructure_mir_constant( + ty::ParamEnv::reveal_all().and(ConstantKind::Val(ct, ty)), + ) { + let fields = contents.fields.iter().copied().collect::<Vec<_>>(); + match *ty.kind() { + ty::Array(..) => { + fmt.write_str("[")?; + comma_sep(fmt, fields)?; + fmt.write_str("]")?; + } + ty::Tuple(..) => { + fmt.write_str("(")?; + comma_sep(fmt, fields)?; + if contents.fields.len() == 1 { + fmt.write_str(",")?; + } + fmt.write_str(")")?; + } + ty::Adt(def, _) if def.variants().is_empty() => { + fmt.write_str(&format!("{{unreachable(): {}}}", ty))?; + } + ty::Adt(def, substs) => { + let variant_idx = contents + .variant + .expect("destructed mir constant of adt without variant idx"); + let variant_def = &def.variant(variant_idx); + let substs = tcx.lift(substs).unwrap(); + let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.print_alloc_ids = true; + let cx = cx.print_value_path(variant_def.def_id, substs)?; + fmt.write_str(&cx.into_buffer())?; + + match variant_def.ctor_kind { + CtorKind::Const => {} + CtorKind::Fn => { + fmt.write_str("(")?; + comma_sep(fmt, fields)?; + fmt.write_str(")")?; + } + CtorKind::Fictive => { + fmt.write_str(" {{ ")?; + let mut first = true; + for (field_def, field) in iter::zip(&variant_def.fields, fields) + { + if !first { + fmt.write_str(", ")?; + } + fmt.write_str(&format!("{}: {}", field_def.name, field))?; + first = false; + } + fmt.write_str(" }}")?; + } + } + } + _ => unreachable!(), + } + return Ok(()); + } else { + // Fall back to debug pretty printing for invalid constants. + fmt.write_str(&format!("{:?}", ct))?; + if print_ty { + fmt.write_str(&format!(": {}", ty))?; + } + return Ok(()); + }; + } + (ConstValue::Scalar(scalar), _) => { + let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.print_alloc_ids = true; + let ty = tcx.lift(ty).unwrap(); + cx = cx.pretty_print_const_scalar(scalar, ty, print_ty)?; + fmt.write_str(&cx.into_buffer())?; + return Ok(()); + } + // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading + // their fields instead of just dumping the memory. + _ => {} + } + // fallback + fmt.write_str(&format!("{:?}", ct))?; + if print_ty { + fmt.write_str(&format!(": {}", ty))?; + } Ok(()) }) } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 739c543dea7..a81a60df2be 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -454,7 +454,12 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { ConstValue::ByRef { .. } => format!("ByRef(..)"), }; - let kind = match literal { + let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { + ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({:?})", leaf), + ty::ValTree::Branch(_) => format!("ValTree::Branch(..)"), + }; + + let val = match literal { ConstantKind::Ty(ct) => match ct.kind() { ty::ConstKind::Param(p) => format!("Param({})", p), ty::ConstKind::Unevaluated(uv) => format!( @@ -463,7 +468,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { uv.substs, uv.promoted, ), - ty::ConstKind::Value(val) => format!("Value({})", fmt_val(&val)), + ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)), ty::ConstKind::Error(_) => "Error".to_string(), // These variants shouldn't exist in the MIR. ty::ConstKind::Placeholder(_) @@ -665,7 +670,8 @@ pub fn write_allocations<'tcx>( ) -> impl DoubleEndedIterator<Item = AllocId> + '_ { alloc.inner().relocations().values().map(|id| *id) } - fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ { + + fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ { match val { ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => { Either::Left(Either::Left(std::iter::once(ptr.provenance))) @@ -681,17 +687,11 @@ pub fn write_allocations<'tcx>( struct CollectAllocIds(BTreeSet<AllocId>); impl<'tcx> Visitor<'tcx> for CollectAllocIds { - fn visit_const(&mut self, c: ty::Const<'tcx>, _loc: Location) { - if let ty::ConstKind::Value(val) = c.kind() { - self.0.extend(alloc_ids_from_const(val)); - } - } - fn visit_constant(&mut self, c: &Constant<'tcx>, loc: Location) { match c.literal { ConstantKind::Ty(c) => self.visit_const(c, loc), ConstantKind::Val(val, _) => { - self.0.extend(alloc_ids_from_const(val)); + self.0.extend(alloc_ids_from_const_val(val)); } } } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 01945b543b1..da4793fa039 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,6 +1,6 @@ //! Values computed by queries that use MIR. -use crate::mir::{self, Body, Promoted}; +use crate::mir::{Body, ConstantKind, Promoted}; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::vec_map::VecMap; @@ -427,7 +427,7 @@ pub struct DestructuredConst<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredMirConstant<'tcx> { pub variant: Option<VariantIdx>, - pub fields: &'tcx [mir::ConstantKind<'tcx>], + pub fields: &'tcx [ConstantKind<'tcx>], } /// Coverage information summarized from a MIR if instrumented for source code coverage (see diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index c859d93043e..c65e79a80fb 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -1,3 +1,4 @@ +use crate::mir; use crate::mir::interpret::Scalar; use crate::ty::{self, Ty, TyCtxt}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -676,7 +677,7 @@ impl<'tcx> TerminatorKind<'tcx> { .values .iter() .map(|&u| { - ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) + mir::ConstantKind::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) .to_string() .into() }) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5b48f164016..22b1ad41904 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -968,7 +968,6 @@ rustc_queries! { key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>> ) -> EvalToValTreeResult<'tcx> { desc { "evaluate type-level constant" } - remap_env_constness } /// Converts a type level constant value into `ConstValue` @@ -980,7 +979,7 @@ rustc_queries! { /// 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::ParamEnvAnd<'tcx, ty::Const<'tcx>>) -> Option<mir::DestructuredConst<'tcx>> { + query try_destructure_const(key: ty::Const<'tcx>) -> Option<mir::DestructuredConst<'tcx>> { desc { "destructure type level constant"} } @@ -993,15 +992,6 @@ rustc_queries! { /// Dereference a constant reference or raw pointer and turn the result into a constant /// again. - query deref_const( - key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>> - ) -> ty::Const<'tcx> { - desc { "deref constant" } - remap_env_constness - } - - /// Dereference a constant reference or raw pointer and turn the result into a constant - /// again. query deref_mir_constant( key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> ) -> mir::ConstantKind<'tcx> { diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs index 015bdb5783f..70abdb9ab4c 100644 --- a/compiler/rustc_middle/src/traits/chalk.rs +++ b/compiler/rustc_middle/src/traits/chalk.rs @@ -5,7 +5,6 @@ //! its name suggest, is to provide an abstraction boundary for creating //! interned Chalk types. -use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::{self, AdtDef, TyCtxt}; use rustc_hir::def_id::DefId; @@ -62,7 +61,7 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { type InternedType = Box<chalk_ir::TyData<Self>>; type InternedLifetime = Box<chalk_ir::LifetimeData<Self>>; type InternedConst = Box<chalk_ir::ConstData<Self>>; - type InternedConcreteConst = ConstValue<'tcx>; + type InternedConcreteConst = ty::ValTree<'tcx>; type InternedGenericArg = Box<chalk_ir::GenericArgData<Self>>; type InternedGoal = Box<chalk_ir::GoalData<Self>>; type InternedGoals = Vec<chalk_ir::Goal<Self>>; diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 0e87a05bade..47abbb723dc 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,5 +1,5 @@ -use crate::mir::interpret::ConstValue; -use crate::mir::interpret::{LitToConstInput, Scalar}; +use crate::mir::interpret::LitToConstInput; +use crate::mir::ConstantKind; use crate::ty::{ self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, @@ -195,14 +195,13 @@ impl<'tcx> Const<'tcx> { /// Interns the given value as a constant. #[inline] - pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self { + pub fn from_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Self { tcx.mk_const(ConstS { kind: ConstKind::Value(val), ty }) } - #[inline] - /// Interns the given scalar as a constant. - pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> Self { - Self::from_value(tcx, ConstValue::Scalar(val), ty) + pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt, ty: Ty<'tcx>) -> Self { + let valtree = ty::ValTree::from_scalar_int(i); + Self::from_value(tcx, valtree, ty) } #[inline] @@ -212,13 +211,14 @@ impl<'tcx> Const<'tcx> { .layout_of(ty) .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) .size; - Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) + Self::from_scalar_int(tcx, ScalarInt::try_from_uint(bits, size).unwrap(), ty.value) } #[inline] /// Creates an interned zst constant. pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { - Self::from_scalar(tcx, Scalar::ZST, ty) + let valtree = ty::ValTree::zst(); + Self::from_value(tcx, valtree, ty) } #[inline] @@ -263,17 +263,32 @@ impl<'tcx> Const<'tcx> { /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the /// unevaluated constant. pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> { - if let Some(val) = self.kind().try_eval(tcx, param_env) { + if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) { match val { Ok(val) => Const::from_value(tcx, val, self.ty()), Err(ErrorGuaranteed { .. }) => tcx.const_error(self.ty()), } } else { + // Either the constant isn't evaluatable or ValTree creation failed. self } } #[inline] + /// Tries to evaluate the constant if it is `Unevaluated` and creates a ConstValue if the + /// evaluation succeeds. If it doesn't succeed, returns the unevaluated constant. + pub fn eval_for_mir(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> ConstantKind<'tcx> { + if let Some(val) = self.val().try_eval_for_mir(tcx, param_env) { + match val { + Ok(const_val) => ConstantKind::from_value(const_val, self.ty()), + Err(ErrorGuaranteed { .. }) => ConstantKind::Ty(tcx.const_error(self.ty())), + } + } else { + ConstantKind::Ty(self) + } + } + + #[inline] /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { self.try_eval_bits(tcx, param_env, ty) diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 35d286d2c57..10d03065c79 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -63,7 +63,7 @@ pub enum ConstKind<'tcx> { Unevaluated(Unevaluated<'tcx>), /// Used to hold computed value. - Value(ConstValue<'tcx>), + Value(ty::ValTree<'tcx>), /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. @@ -75,7 +75,7 @@ static_assert_size!(ConstKind<'_>, 40); impl<'tcx> ConstKind<'tcx> { #[inline] - pub fn try_to_value(self) -> Option<ConstValue<'tcx>> { + pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> { if let ConstKind::Value(val) = self { Some(val) } else { None } } @@ -86,7 +86,7 @@ impl<'tcx> ConstKind<'tcx> { #[inline] pub fn try_to_scalar_int(self) -> Option<ScalarInt> { - Some(self.try_to_value()?.try_to_scalar()?.assert_int()) + self.try_to_value()?.try_to_scalar_int() } #[inline] @@ -115,23 +115,65 @@ pub enum InferConst<'tcx> { Fresh(u32), } +enum EvalMode { + Typeck, + Mir, +} + +enum EvalResult<'tcx> { + ValTree(ty::ValTree<'tcx>), + ConstVal(ConstValue<'tcx>), +} + impl<'tcx> ConstKind<'tcx> { #[inline] /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the /// unevaluated constant. pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - self.try_eval(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value) + self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value) } #[inline] /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary /// return `None`. // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged. - pub fn try_eval( + pub fn try_eval_for_mir( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> { + match self.try_eval_inner(tcx, param_env, EvalMode::Mir) { + Some(Ok(EvalResult::ValTree(_))) => unreachable!(), + Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)), + Some(Err(e)) => Some(Err(e)), + None => None, + } + } + + #[inline] + /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary + /// return `None`. + // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged. + pub fn try_eval_for_typeck( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> { + match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) { + Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)), + Some(Ok(EvalResult::ConstVal(_))) => unreachable!(), + Some(Err(e)) => Some(Err(e)), + None => None, + } + } + + #[inline] + fn try_eval_inner( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + eval_mode: EvalMode, + ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> { if let ConstKind::Unevaluated(unevaluated) = self { use crate::mir::interpret::ErrorHandled; @@ -166,14 +208,29 @@ impl<'tcx> ConstKind<'tcx> { let (param_env, unevaluated) = param_env_and.into_parts(); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - match tcx.const_eval_resolve(param_env, unevaluated, None) { - // NOTE(eddyb) `val` contains no lifetimes/types/consts, - // and we use the original type, so nothing from `substs` - // (which may be identity substs, see above), - // can leak through `val` into the const we return. - Ok(val) => Some(Ok(val)), - Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, - Err(ErrorHandled::Reported(e)) => Some(Err(e)), + match eval_mode { + EvalMode::Typeck => { + match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) { + // NOTE(eddyb) `val` contains no lifetimes/types/consts, + // and we use the original type, so nothing from `substs` + // (which may be identity substs, see above), + // can leak through `val` into the const we return. + Ok(val) => Some(Ok(EvalResult::ValTree(val?))), + Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, + Err(ErrorHandled::Reported(e)) => Some(Err(e)), + } + } + EvalMode::Mir => { + match tcx.const_eval_resolve(param_env, unevaluated, None) { + // NOTE(eddyb) `val` contains no lifetimes/types/consts, + // and we use the original type, so nothing from `substs` + // (which may be identity substs, see above), + // can leak through `val` into the const we return. + Ok(val) => Some(Ok(EvalResult::ConstVal(val))), + Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, + Err(ErrorHandled::Reported(e)) => Some(Err(e)), + } + } } } else { None diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 418848f69d7..973dc3dd4a1 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -1,4 +1,6 @@ use super::ScalarInt; +use crate::mir::interpret::{AllocId, Scalar}; +use crate::ty::{self, Ty, TyCtxt}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; #[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] @@ -50,4 +52,61 @@ impl<'tcx> ValTree<'tcx> { _ => bug!("expected branch, got {:?}", self), } } + + pub fn from_raw_bytes<'a>(tcx: TyCtxt<'tcx>, bytes: &'a [u8]) -> Self { + let branches = bytes.iter().map(|b| Self::Leaf(ScalarInt::from(*b))); + let interned = tcx.arena.alloc_from_iter(branches); + + Self::Branch(interned) + } + + pub fn from_scalar_int(i: ScalarInt) -> Self { + Self::Leaf(i) + } + + pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> { + self.try_to_scalar_int().map(Scalar::Int) + } + + pub fn try_to_scalar_int(self) -> Option<ScalarInt> { + match self { + Self::Leaf(s) => Some(s), + Self::Branch(_) => None, + } + } + + pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> { + self.try_to_scalar_int().map(|s| s.try_to_machine_usize(tcx).ok()).flatten() + } + + /// Get the values inside the ValTree as a slice of bytes. This only works for + /// constants with types &str and &[u8]. + pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { + match ty.kind() { + ty::Ref(_, inner_ty, _) => match inner_ty.kind() { + ty::Str => { + let leafs = self + .unwrap_branch() + .into_iter() + .map(|v| v.unwrap_leaf().try_to_u8().unwrap()) + .collect::<Vec<_>>(); + + return Some(tcx.arena.alloc_from_iter(leafs.into_iter())); + } + ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => { + let leafs = self + .unwrap_branch() + .into_iter() + .map(|v| v.unwrap_leaf().try_to_u8().unwrap()) + .collect::<Vec<_>>(); + + return Some(tcx.arena.alloc_from_iter(leafs.into_iter())); + } + _ => {} + }, + _ => {} + } + + None + } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5c0cf534b80..0930f3edf72 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -8,7 +8,7 @@ use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::resolve_lifetime; use crate::middle::stability; -use crate::mir::interpret::{self, Allocation, ConstAllocation, ConstValue, Scalar}; +use crate::mir::interpret::{self, Allocation, ConstAllocation}; use crate::mir::{ Body, BorrowCheckResult, Field, Local, Place, PlaceElem, ProjectionKind, Promoted, }; @@ -991,7 +991,7 @@ impl<'tcx> CommonConsts<'tcx> { CommonConsts { unit: mk_const(ty::ConstS { - kind: ty::ConstKind::Value(ConstValue::Scalar(Scalar::ZST)), + kind: ty::ConstKind::Value(ty::ValTree::zst()), ty: types.unit, }), } @@ -1186,11 +1186,7 @@ impl<'tcx> TyCtxt<'tcx> { }; debug!("layout_scalar_valid_range: attr={:?}", attr); if let Some( - &[ - ast::NestedMetaItem::Literal(ast::Lit { - kind: ast::LitKind::Int(a, _), .. - }), - ], + &[ast::NestedMetaItem::Literal(ast::Lit { kind: ast::LitKind::Int(a, _), .. })], ) = attr.meta_item_list().as_deref() { Bound::Included(a) @@ -1663,7 +1659,7 @@ macro_rules! nop_lift { impl<'a, 'tcx> Lift<'tcx> for $ty { type Lifted = $lifted; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { - if tcx.interners.$set.contains_pointer_to(&InternedInSet(&*self.0.0)) { + if tcx.interners.$set.contains_pointer_to(&InternedInSet(&*self.0 .0)) { // SAFETY: `self` is interned and therefore valid // for the entire lifetime of the `TyCtxt`. Some(unsafe { mem::transmute(self) }) @@ -2248,7 +2244,11 @@ impl<'tcx> TyCtxt<'tcx> { /// `*r == kind`. #[inline] pub fn reuse_or_mk_region(self, r: Region<'tcx>, kind: RegionKind) -> Region<'tcx> { - if *r == kind { r } else { self.mk_region(kind) } + if *r == kind { + r + } else { + self.mk_region(kind) + } } #[allow(rustc::usage_of_ty_tykind)] @@ -2268,7 +2268,11 @@ impl<'tcx> TyCtxt<'tcx> { pred: Predicate<'tcx>, binder: Binder<'tcx, PredicateKind<'tcx>>, ) -> Predicate<'tcx> { - if pred.kind() != binder { self.mk_predicate(binder) } else { pred } + if pred.kind() != binder { + self.mk_predicate(binder) + } else { + pred + } } pub fn mk_mach_int(self, tm: IntTy) -> Ty<'tcx> { @@ -2413,7 +2417,11 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn mk_diverging_default(self) -> Ty<'tcx> { - if self.features().never_type_fallback { self.types.never } else { self.types.unit } + if self.features().never_type_fallback { + self.types.never + } else { + self.types.unit + } } #[inline] @@ -2564,11 +2572,9 @@ impl<'tcx> TyCtxt<'tcx> { eps: &[ty::Binder<'tcx, ExistentialPredicate<'tcx>>], ) -> &'tcx List<ty::Binder<'tcx, ExistentialPredicate<'tcx>>> { assert!(!eps.is_empty()); - assert!( - eps.array_windows() - .all(|[a, b]| a.skip_binder().stable_cmp(self, &b.skip_binder()) - != Ordering::Greater) - ); + assert!(eps + .array_windows() + .all(|[a, b]| a.skip_binder().stable_cmp(self, &b.skip_binder()) != Ordering::Greater)); self._intern_poly_existential_predicates(eps) } @@ -2601,29 +2607,49 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn intern_substs(self, ts: &[GenericArg<'tcx>]) -> &'tcx List<GenericArg<'tcx>> { - if ts.is_empty() { List::empty() } else { self._intern_substs(ts) } + if ts.is_empty() { + List::empty() + } else { + self._intern_substs(ts) + } } pub fn intern_projs(self, ps: &[ProjectionKind]) -> &'tcx List<ProjectionKind> { - if ps.is_empty() { List::empty() } else { self._intern_projs(ps) } + if ps.is_empty() { + List::empty() + } else { + self._intern_projs(ps) + } } pub fn intern_place_elems(self, ts: &[PlaceElem<'tcx>]) -> &'tcx List<PlaceElem<'tcx>> { - if ts.is_empty() { List::empty() } else { self._intern_place_elems(ts) } + if ts.is_empty() { + List::empty() + } else { + self._intern_place_elems(ts) + } } pub fn intern_canonical_var_infos( self, ts: &[CanonicalVarInfo<'tcx>], ) -> CanonicalVarInfos<'tcx> { - if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) } + if ts.is_empty() { + List::empty() + } else { + self._intern_canonical_var_infos(ts) + } } pub fn intern_bound_variable_kinds( self, ts: &[ty::BoundVariableKind], ) -> &'tcx List<ty::BoundVariableKind> { - if ts.is_empty() { List::empty() } else { self._intern_bound_variable_kinds(ts) } + if ts.is_empty() { + List::empty() + } else { + self._intern_bound_variable_kinds(ts) + } } pub fn mk_fn_sig<I>( diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index b7130e69f35..e8dd179eac1 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -369,7 +369,6 @@ impl<'tcx> Instance<'tcx> { } // This should be kept up to date with `resolve`. - #[instrument(level = "debug", skip(tcx))] pub fn resolve_opt_const_arg( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7f0f3755c4b..8da447d16fb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2161,22 +2161,28 @@ impl<'tcx> TyCtxt<'tcx> { } /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. + #[instrument(skip(self), level = "debug")] pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { match instance { - ty::InstanceDef::Item(def) => match self.def_kind(def.did) { - DefKind::Const - | DefKind::Static(..) - | DefKind::AssocConst - | DefKind::Ctor(..) - | DefKind::AnonConst - | DefKind::InlineConst => self.mir_for_ctfe_opt_const_arg(def), - // If the caller wants `mir_for_ctfe` of a function they should not be using - // `instance_mir`, so we'll assume const fn also wants the optimized version. - _ => { - assert_eq!(def.const_param_did, None); - self.optimized_mir(def.did) + ty::InstanceDef::Item(def) => { + debug!("calling def_kind on def: {:?}", def); + let def_kind = self.def_kind(def.did); + debug!("returned from def_kind: {:?}", def_kind); + match def_kind { + DefKind::Const + | DefKind::Static(..) + | DefKind::AssocConst + | DefKind::Ctor(..) + | DefKind::AnonConst + | DefKind::InlineConst => self.mir_for_ctfe_opt_const_arg(def), + // If the caller wants `mir_for_ctfe` of a function they should not be using + // `instance_mir`, so we'll assume const fn also wants the optimized version. + _ => { + assert_eq!(def.const_param_did, None); + self.optimized_mir(def.did) + } } - }, + } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 5ad93d77820..7a096bbc4d9 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -98,7 +98,6 @@ pub trait Printer<'tcx>: Sized { // Defaults (should not be overridden): - #[instrument(skip(self), level = "debug")] fn default_print_def_path( self, def_id: DefId, @@ -302,6 +301,7 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> { impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { type Output = P::Type; type Error = P::Error; + fn print(&self, cx: P) -> Result<Self::Output, Self::Error> { cx.print_type(*self) } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 05f332cdd5f..3b11f7572a6 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,4 +1,4 @@ -use crate::mir::interpret::{AllocRange, ConstValue, GlobalAlloc, Pointer, Provenance, Scalar}; +use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; use crate::ty::{ self, ConstInt, DefIdTree, ParamConst, ScalarInt, Term, Ty, TyCtxt, TypeFoldable, @@ -607,7 +607,11 @@ pub trait PrettyPrinter<'tcx>: } } } else { - if verbose { p!(write("{:?}", infer_ty)) } else { p!(write("{}", infer_ty)) } + if verbose { + p!(write("{:?}", infer_ty)) + } else { + p!(write("{}", infer_ty)) + } } } ty::Error(_) => p!("[type error]"), @@ -1224,7 +1228,7 @@ pub trait PrettyPrinter<'tcx>: } ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), ty::ConstKind::Value(value) => { - return self.pretty_print_const_value(value, ct.ty(), print_ty); + return self.pretty_print_const_valtree(value, ct.ty(), print_ty); } ty::ConstKind::Bound(debruijn, bound_var) => { @@ -1262,7 +1266,7 @@ pub trait PrettyPrinter<'tcx>: ty::Ref(_, inner, _) => { if let ty::Array(elem, len) = inner.kind() { if let ty::Uint(ty::UintTy::U8) = elem.kind() { - if let ty::ConstKind::Value(ConstValue::Scalar(int)) = len.kind() { + if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.kind() { match self.tcx().get_global_alloc(alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { let len = int.assert_bits(self.tcx().data_layout.pointer_size); @@ -1331,7 +1335,11 @@ pub trait PrettyPrinter<'tcx>: ty::Uint(_) | ty::Int(_) => { let int = ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral()); - if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } + if print_ty { + p!(write("{:#?}", int)) + } else { + p!(write("{:?}", int)) + } } // Char ty::Char if char::try_from(int).is_ok() => { @@ -1408,85 +1416,64 @@ pub trait PrettyPrinter<'tcx>: Ok(self) } - fn pretty_print_const_value( + fn pretty_print_const_valtree( mut self, - ct: ConstValue<'tcx>, + valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>, print_ty: bool, ) -> Result<Self::Const, Self::Error> { define_scoped_cx!(self); if self.tcx().sess.verbose() { - p!(write("ConstValue({:?}: ", ct), print(ty), ")"); + p!(write("ValTree({:?}: ", valtree), print(ty), ")"); return Ok(self); } let u8_type = self.tcx().types.u8; - - match (ct, ty.kind()) { - // Byte/string slices, printed as (byte) string literals. - (ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => { - match inner.kind() { - ty::Slice(t) => { - if *t == u8_type { - // The `inspect` here is okay since we checked the bounds, and there are - // no relocations (we have an active slice reference here). We don't use - // this result to affect interpreter execution. - let byte_str = data - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(start..end); - return self.pretty_print_byte_str(byte_str); - } - } - ty::Str => { - // The `inspect` here is okay since we checked the bounds, and there are no - // relocations (we have an active `str` reference here). We don't use this - // result to affect interpreter execution. - let slice = data - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(start..end); - p!(write("{:?}", String::from_utf8_lossy(slice))); - return Ok(self); - } - _ => {} + match (valtree, ty.kind()) { + (ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { + ty::Slice(t) if *t == u8_type => { + let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { + bug!( + "expected to convert valtree {:?} to raw bytes for type {:?}", + valtree, + t + ) + }); + return self.pretty_print_byte_str(bytes); } - } - (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { - let n = n.kind().try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); - // cast is ok because we already checked for pointer size (32 or 64 bit) above - let range = AllocRange { start: offset, size: Size::from_bytes(n) }; - - let byte_str = alloc.inner().get_bytes(&self.tcx(), range).unwrap(); + ty::Str => { + let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { + bug!("expected to convert valtree to raw bytes for type {:?}", ty) + }); + p!(write("{:?}", String::from_utf8_lossy(bytes))); + 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(|| { + bug!("expected to convert valtree to raw bytes for type {:?}", t) + }); p!("*"); - p!(pretty_print_byte_str(byte_str)); + p!(pretty_print_byte_str(bytes)); return Ok(self); } - // Aggregates, printed as array/tuple/struct/variant construction syntax. - // - // NB: the `has_param_types_or_consts` check ensures that we can use - // the `destructure_const` query with an empty `ty::ParamEnv` 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) - // - // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the - // correct `ty::ParamEnv` to allow printing *all* constant values. - (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) + if !ty.has_param_types_or_consts() => + { let Some(contents) = self.tcx().try_destructure_const( - ty::ParamEnv::reveal_all() - .and(self.tcx().mk_const(ty::ConstS { kind: ty::ConstKind::Value(ct), ty })), + ty::Const::from_value(self.tcx(), valtree, ty) ) else { // Fall back to debug pretty printing for invalid constants. - p!(write("{:?}", ct)); + p!(write("{:?}", valtree)); if print_ty { p!(": ", print(ty)); } return Ok(self); }; - let fields = contents.fields.iter().copied(); - match *ty.kind() { ty::Array(..) => { p!("[", comma_sep(fields), "]"); @@ -1513,7 +1500,6 @@ pub trait PrettyPrinter<'tcx>: contents.variant.expect("destructed const of adt without variant idx"); let variant_def = &def.variant(variant_idx); p!(print_value_path(variant_def.def_id, substs)); - match variant_def.ctor_kind { CtorKind::Const => {} CtorKind::Fn => { @@ -1535,21 +1521,20 @@ pub trait PrettyPrinter<'tcx>: } _ => unreachable!(), } - return Ok(self); } - - (ConstValue::Scalar(scalar), _) => { - return self.pretty_print_const_scalar(scalar, ty, print_ty); + (ty::ValTree::Leaf(leaf), _) => { + return self.pretty_print_const_scalar_int(leaf, ty, print_ty); } - // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading // their fields instead of just dumping the memory. _ => {} } // fallback - p!(write("{:?}", ct)); + if valtree != ty::ValTree::zst() { + p!(write("{:?}", valtree)); + } if print_ty { p!(": ", print(ty)); } @@ -2296,7 +2281,6 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { Ok(inner) } - #[instrument(skip(self), level = "debug")] fn prepare_late_bound_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>) where T: TypeFoldable<'tcx>, @@ -2309,9 +2293,8 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> { type BreakTy = (); - #[instrument(skip(self), level = "trace")] fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - trace!("address: {:p}", r.0.0); + trace!("address: {:p}", r.0 .0); if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r { self.used_region_names.insert(name); } else if let ty::RePlaceholder(ty::PlaceholderRegion { @@ -2326,7 +2309,6 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // We collect types in order to prevent really large types from compiling for // a really long time. See issue #83150 for why this is necessary. - #[instrument(skip(self), level = "trace")] fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { let not_previously_inserted = self.type_collector.insert(ty); if not_previously_inserted { @@ -2353,6 +2335,7 @@ where { type Output = P; type Error = P::Error; + fn print(&self, cx: P) -> Result<Self::Output, Self::Error> { cx.in_binder(self) } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 31103b8d77e..51980acd38f 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -4,7 +4,6 @@ //! types or regions but can be other things. Examples of type relations are //! subtyping, type equality, etc. -use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar}; use crate::ty::error::{ExpectedFound, TypeError}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use crate::ty::{self, ImplSubject, Term, Ty, TyCtxt, TypeFoldable}; @@ -613,9 +612,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) => a_p.index == b_p.index, (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { - check_const_value_eq(relation, a_val, b_val, a, b)? - } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if tcx.features().generic_const_exprs => @@ -649,66 +646,6 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) } } -fn check_const_value_eq<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a_val: ConstValue<'tcx>, - b_val: ConstValue<'tcx>, - // FIXME(oli-obk): these arguments should go away with valtrees - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - // FIXME(oli-obk): this should just be `bool` with valtrees -) -> RelateResult<'tcx, bool> { - let tcx = relation.tcx(); - Ok(match (a_val, b_val) { - (ConstValue::Scalar(Scalar::Int(a_val)), ConstValue::Scalar(Scalar::Int(b_val))) => { - a_val == b_val - } - ( - ConstValue::Scalar(Scalar::Ptr(a_val, _a_size)), - ConstValue::Scalar(Scalar::Ptr(b_val, _b_size)), - ) => { - a_val == b_val - || match (tcx.global_alloc(a_val.provenance), tcx.global_alloc(b_val.provenance)) { - (GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => { - a_instance == b_instance - } - _ => false, - } - } - - (ConstValue::Slice { .. }, ConstValue::Slice { .. }) => { - get_slice_bytes(&tcx, a_val) == get_slice_bytes(&tcx, b_val) - } - - (ConstValue::ByRef { alloc: alloc_a, .. }, ConstValue::ByRef { alloc: alloc_b, .. }) - if a.ty().is_ref() || b.ty().is_ref() => - { - if a.ty().is_ref() && b.ty().is_ref() { - alloc_a == alloc_b - } else { - false - } - } - (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => { - let a_destructured = tcx.destructure_const(relation.param_env().and(a)); - let b_destructured = tcx.destructure_const(relation.param_env().and(b)); - - // Both the variant and each field have to be equal. - if a_destructured.variant == b_destructured.variant { - for (a_field, b_field) in iter::zip(a_destructured.fields, b_destructured.fields) { - relation.consts(*a_field, *b_field)?; - } - - true - } else { - false - } - } - - _ => false, - }) -} - impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>> { fn relate<R: TypeRelation<'tcx>>( relation: &mut R, |
