1 use std::convert::TryInto; 2 use std::fmt; 3 4 use crate::mir::interpret::{AllocId, ConstValue, Scalar}; 5 use crate::mir::Promoted; 6 use crate::ty::subst::{InternalSubsts, SubstsRef}; 7 use crate::ty::ParamEnv; 8 use crate::ty::{self, TyCtxt, TypeFoldable}; 9 use rustc_errors::ErrorReported; 10 use rustc_hir::def_id::DefId; 11 use rustc_macros::HashStable; 12 use rustc_target::abi::Size; 13 14 use super::ScalarInt; 15 /// An unevaluated, potentially generic, constant. 16 /// 17 /// If `substs_` is `None` it means that this anon const 18 /// still has its default substs. 19 /// 20 /// We check for all possible substs in `fn default_anon_const_substs`, 21 /// so refer to that check for more info. 22 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)] 23 #[derive(Hash, HashStable)] 24 pub struct Unevaluated<'tcx, P = Option<Promoted>> { 25 pub def: ty::WithOptConstParam<DefId>, 26 pub substs_: Option<SubstsRef<'tcx>>, 27 pub promoted: P, 28 } 29 30 impl<'tcx> Unevaluated<'tcx> { 31 #[inline] shrink(self) -> Unevaluated<'tcx, ()>32 pub fn shrink(self) -> Unevaluated<'tcx, ()> { 33 debug_assert_eq!(self.promoted, None); 34 Unevaluated { def: self.def, substs_: self.substs_, promoted: () } 35 } 36 } 37 38 impl<'tcx> Unevaluated<'tcx, ()> { 39 #[inline] expand(self) -> Unevaluated<'tcx>40 pub fn expand(self) -> Unevaluated<'tcx> { 41 Unevaluated { def: self.def, substs_: self.substs_, promoted: None } 42 } 43 } 44 45 impl<'tcx, P: Default> Unevaluated<'tcx, P> { 46 #[inline] new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P>47 pub fn new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P> { 48 Unevaluated { def, substs_: Some(substs), promoted: Default::default() } 49 } 50 } 51 52 impl<'tcx, P: Default + PartialEq + fmt::Debug> Unevaluated<'tcx, P> { 53 #[inline] substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx>54 pub fn substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx> { 55 self.substs_.unwrap_or_else(|| { 56 // We must not use the parents default substs for promoted constants 57 // as that can result in incorrect substs and calls the `default_anon_const_substs` 58 // for something that might not actually be a constant. 59 debug_assert_eq!(self.promoted, Default::default()); 60 tcx.default_anon_const_substs(self.def.did) 61 }) 62 } 63 } 64 65 /// Represents a constant in Rust. 66 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] 67 #[derive(Hash, HashStable)] 68 pub enum ConstKind<'tcx> { 69 /// A const generic parameter. 70 Param(ty::ParamConst), 71 72 /// Infer the value of the const. 73 Infer(InferConst<'tcx>), 74 75 /// Bound const variable, used only when preparing a trait query. 76 Bound(ty::DebruijnIndex, ty::BoundVar), 77 78 /// A placeholder const - universally quantified higher-ranked const. 79 Placeholder(ty::PlaceholderConst<'tcx>), 80 81 /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other 82 /// variants when the code is monomorphic enough for that. 83 Unevaluated(Unevaluated<'tcx>), 84 85 /// Used to hold computed value. 86 Value(ConstValue<'tcx>), 87 88 /// A placeholder for a const which could not be computed; this is 89 /// propagated to avoid useless error messages. 90 Error(ty::DelaySpanBugEmitted), 91 } 92 93 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 94 static_assert_size!(ConstKind<'_>, 40); 95 96 impl<'tcx> ConstKind<'tcx> { 97 #[inline] try_to_value(self) -> Option<ConstValue<'tcx>>98 pub fn try_to_value(self) -> Option<ConstValue<'tcx>> { 99 if let ConstKind::Value(val) = self { Some(val) } else { None } 100 } 101 102 #[inline] try_to_scalar(self) -> Option<Scalar<AllocId>>103 pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> { 104 self.try_to_value()?.try_to_scalar() 105 } 106 107 #[inline] try_to_scalar_int(self) -> Option<ScalarInt>108 pub fn try_to_scalar_int(self) -> Option<ScalarInt> { 109 Some(self.try_to_value()?.try_to_scalar()?.assert_int()) 110 } 111 112 #[inline] try_to_bits(self, size: Size) -> Option<u128>113 pub fn try_to_bits(self, size: Size) -> Option<u128> { 114 self.try_to_scalar_int()?.to_bits(size).ok() 115 } 116 117 #[inline] try_to_bool(self) -> Option<bool>118 pub fn try_to_bool(self) -> Option<bool> { 119 self.try_to_scalar_int()?.try_into().ok() 120 } 121 122 #[inline] try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64>123 pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> { 124 self.try_to_value()?.try_to_machine_usize(tcx) 125 } 126 } 127 128 /// An inference variable for a const, for use in const generics. 129 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] 130 #[derive(HashStable)] 131 pub enum InferConst<'tcx> { 132 /// Infer the value of the const. 133 Var(ty::ConstVid<'tcx>), 134 /// A fresh const variable. See `infer::freshen` for more details. 135 Fresh(u32), 136 } 137 138 impl<'tcx> ConstKind<'tcx> { 139 #[inline] 140 /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the 141 /// unevaluated constant. eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self142 pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { 143 self.try_eval(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value) 144 } 145 146 #[inline] 147 /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary 148 /// return `None`. try_eval( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ) -> Option<Result<ConstValue<'tcx>, ErrorReported>>149 pub(super) fn try_eval( 150 self, 151 tcx: TyCtxt<'tcx>, 152 param_env: ParamEnv<'tcx>, 153 ) -> Option<Result<ConstValue<'tcx>, ErrorReported>> { 154 if let ConstKind::Unevaluated(unevaluated) = self { 155 use crate::mir::interpret::ErrorHandled; 156 157 // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` 158 // also does later, but we want to do it before checking for 159 // inference variables. 160 // Note that we erase regions *before* calling `with_reveal_all_normalized`, 161 // so that we don't try to invoke this query with 162 // any region variables. 163 let param_env_and = tcx 164 .erase_regions(param_env) 165 .with_reveal_all_normalized(tcx) 166 .and(tcx.erase_regions(unevaluated)); 167 168 // HACK(eddyb) when the query key would contain inference variables, 169 // attempt using identity substs and `ParamEnv` instead, that will succeed 170 // when the expression doesn't depend on any parameters. 171 // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that 172 // we can call `infcx.const_eval_resolve` which handles inference variables. 173 let param_env_and = if param_env_and.needs_infer() { 174 tcx.param_env(unevaluated.def.did).and(ty::Unevaluated { 175 def: unevaluated.def, 176 substs_: Some(InternalSubsts::identity_for_item(tcx, unevaluated.def.did)), 177 promoted: unevaluated.promoted, 178 }) 179 } else { 180 param_env_and 181 }; 182 183 // FIXME(eddyb) maybe the `const_eval_*` methods should take 184 // `ty::ParamEnvAnd` instead of having them separate. 185 let (param_env, unevaluated) = param_env_and.into_parts(); 186 // try to resolve e.g. associated constants to their definition on an impl, and then 187 // evaluate the const. 188 match tcx.const_eval_resolve(param_env, unevaluated, None) { 189 // NOTE(eddyb) `val` contains no lifetimes/types/consts, 190 // and we use the original type, so nothing from `substs` 191 // (which may be identity substs, see above), 192 // can leak through `val` into the const we return. 193 Ok(val) => Some(Ok(val)), 194 Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, 195 Err(ErrorHandled::Reported(e)) => Some(Err(e)), 196 } 197 } else { 198 None 199 } 200 } 201 } 202