]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
1 | use std::convert::TryInto; |
2 | ||
487cf647 | 3 | use super::Const; |
2b03887a | 4 | use crate::mir; |
136023e0 | 5 | use crate::mir::interpret::{AllocId, ConstValue, Scalar}; |
487cf647 | 6 | use crate::ty::abstract_const::CastKind; |
3dfed10e XL |
7 | use crate::ty::subst::{InternalSubsts, SubstsRef}; |
8 | use crate::ty::ParamEnv; | |
487cf647 | 9 | use crate::ty::{self, List, Ty, TyCtxt, TypeVisitable}; |
2b03887a | 10 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; |
5e7ed085 | 11 | use rustc_errors::ErrorGuaranteed; |
3dfed10e XL |
12 | use rustc_hir::def_id::DefId; |
13 | use rustc_macros::HashStable; | |
14 | use rustc_target::abi::Size; | |
15 | ||
6a06907d | 16 | use super::ScalarInt; |
f2b60f7d | 17 | |
2b03887a | 18 | /// An unevaluated (potentially generic) constant used in the type-system. |
94222f64 | 19 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)] |
2b03887a FG |
20 | #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)] |
21 | pub struct UnevaluatedConst<'tcx> { | |
cdc7bbd5 | 22 | pub def: ty::WithOptConstParam<DefId>, |
5099ac24 | 23 | pub substs: SubstsRef<'tcx>, |
94222f64 XL |
24 | } |
25 | ||
2b03887a | 26 | impl rustc_errors::IntoDiagnosticArg for UnevaluatedConst<'_> { |
f2b60f7d FG |
27 | fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { |
28 | format!("{:?}", self).into_diagnostic_arg() | |
29 | } | |
30 | } | |
31 | ||
2b03887a | 32 | impl<'tcx> UnevaluatedConst<'tcx> { |
94222f64 | 33 | #[inline] |
2b03887a FG |
34 | pub fn expand(self) -> mir::UnevaluatedConst<'tcx> { |
35 | mir::UnevaluatedConst { def: self.def, substs: self.substs, promoted: None } | |
94222f64 XL |
36 | } |
37 | } | |
38 | ||
2b03887a | 39 | impl<'tcx> UnevaluatedConst<'tcx> { |
94222f64 | 40 | #[inline] |
2b03887a FG |
41 | pub fn new( |
42 | def: ty::WithOptConstParam<DefId>, | |
43 | substs: SubstsRef<'tcx>, | |
44 | ) -> UnevaluatedConst<'tcx> { | |
45 | UnevaluatedConst { def, substs } | |
94222f64 | 46 | } |
cdc7bbd5 | 47 | } |
6a06907d | 48 | |
3dfed10e | 49 | /// Represents a constant in Rust. |
cdc7bbd5 | 50 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] |
f2b60f7d | 51 | #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)] |
487cf647 | 52 | #[derive(derive_more::From)] |
3dfed10e XL |
53 | pub enum ConstKind<'tcx> { |
54 | /// A const generic parameter. | |
55 | Param(ty::ParamConst), | |
56 | ||
57 | /// Infer the value of the const. | |
58 | Infer(InferConst<'tcx>), | |
59 | ||
60 | /// Bound const variable, used only when preparing a trait query. | |
61 | Bound(ty::DebruijnIndex, ty::BoundVar), | |
62 | ||
63 | /// A placeholder const - universally quantified higher-ranked const. | |
fc512014 | 64 | Placeholder(ty::PlaceholderConst<'tcx>), |
3dfed10e XL |
65 | |
66 | /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other | |
67 | /// variants when the code is monomorphic enough for that. | |
2b03887a | 68 | Unevaluated(UnevaluatedConst<'tcx>), |
3dfed10e XL |
69 | |
70 | /// Used to hold computed value. | |
923072b8 | 71 | Value(ty::ValTree<'tcx>), |
3dfed10e XL |
72 | |
73 | /// A placeholder for a const which could not be computed; this is | |
74 | /// propagated to avoid useless error messages. | |
487cf647 FG |
75 | #[from(ignore)] |
76 | Error(ErrorGuaranteed), | |
77 | ||
78 | /// Expr which contains an expression which has partially evaluated items. | |
79 | Expr(Expr<'tcx>), | |
80 | } | |
81 | ||
82 | impl<'tcx> From<ty::ConstVid<'tcx>> for ConstKind<'tcx> { | |
83 | fn from(const_vid: ty::ConstVid<'tcx>) -> Self { | |
84 | InferConst::Var(const_vid).into() | |
85 | } | |
3dfed10e XL |
86 | } |
87 | ||
487cf647 FG |
88 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] |
89 | #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] | |
90 | pub enum Expr<'tcx> { | |
91 | Binop(mir::BinOp, Const<'tcx>, Const<'tcx>), | |
92 | UnOp(mir::UnOp, Const<'tcx>), | |
93 | FunctionCall(Const<'tcx>, &'tcx List<Const<'tcx>>), | |
94 | Cast(CastKind, Const<'tcx>, Ty<'tcx>), | |
95 | } | |
96 | ||
97 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] | |
98 | static_assert_size!(Expr<'_>, 24); | |
99 | ||
6a06907d | 100 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
f2b60f7d | 101 | static_assert_size!(ConstKind<'_>, 32); |
3dfed10e XL |
102 | |
103 | impl<'tcx> ConstKind<'tcx> { | |
104 | #[inline] | |
923072b8 | 105 | pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> { |
3dfed10e XL |
106 | if let ConstKind::Value(val) = self { Some(val) } else { None } |
107 | } | |
108 | ||
109 | #[inline] | |
136023e0 | 110 | pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> { |
3dfed10e XL |
111 | self.try_to_value()?.try_to_scalar() |
112 | } | |
113 | ||
6a06907d XL |
114 | #[inline] |
115 | pub fn try_to_scalar_int(self) -> Option<ScalarInt> { | |
923072b8 | 116 | self.try_to_value()?.try_to_scalar_int() |
6a06907d XL |
117 | } |
118 | ||
3dfed10e XL |
119 | #[inline] |
120 | pub fn try_to_bits(self, size: Size) -> Option<u128> { | |
6a06907d | 121 | self.try_to_scalar_int()?.to_bits(size).ok() |
3dfed10e XL |
122 | } |
123 | ||
124 | #[inline] | |
125 | pub fn try_to_bool(self) -> Option<bool> { | |
6a06907d | 126 | self.try_to_scalar_int()?.try_into().ok() |
3dfed10e XL |
127 | } |
128 | ||
129 | #[inline] | |
130 | pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> { | |
131 | self.try_to_value()?.try_to_machine_usize(tcx) | |
132 | } | |
133 | } | |
134 | ||
135 | /// An inference variable for a const, for use in const generics. | |
136 | #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] | |
3dfed10e XL |
137 | pub enum InferConst<'tcx> { |
138 | /// Infer the value of the const. | |
139 | Var(ty::ConstVid<'tcx>), | |
140 | /// A fresh const variable. See `infer::freshen` for more details. | |
141 | Fresh(u32), | |
142 | } | |
143 | ||
2b03887a FG |
144 | impl<CTX> HashStable<CTX> for InferConst<'_> { |
145 | fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { | |
146 | match self { | |
147 | InferConst::Var(_) => panic!("const variables should not be hashed: {self:?}"), | |
148 | InferConst::Fresh(i) => i.hash_stable(hcx, hasher), | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
923072b8 FG |
153 | enum EvalMode { |
154 | Typeck, | |
155 | Mir, | |
156 | } | |
157 | ||
158 | enum EvalResult<'tcx> { | |
159 | ValTree(ty::ValTree<'tcx>), | |
160 | ConstVal(ConstValue<'tcx>), | |
161 | } | |
162 | ||
3dfed10e XL |
163 | impl<'tcx> ConstKind<'tcx> { |
164 | #[inline] | |
165 | /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the | |
166 | /// unevaluated constant. | |
167 | pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { | |
923072b8 | 168 | self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value) |
3dfed10e XL |
169 | } |
170 | ||
171 | #[inline] | |
172 | /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary | |
173 | /// return `None`. | |
04454e1e | 174 | // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged. |
923072b8 | 175 | pub fn try_eval_for_mir( |
3dfed10e XL |
176 | self, |
177 | tcx: TyCtxt<'tcx>, | |
178 | param_env: ParamEnv<'tcx>, | |
5e7ed085 | 179 | ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> { |
923072b8 FG |
180 | match self.try_eval_inner(tcx, param_env, EvalMode::Mir) { |
181 | Some(Ok(EvalResult::ValTree(_))) => unreachable!(), | |
182 | Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)), | |
183 | Some(Err(e)) => Some(Err(e)), | |
184 | None => None, | |
185 | } | |
186 | } | |
187 | ||
188 | #[inline] | |
189 | /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary | |
190 | /// return `None`. | |
191 | // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged. | |
192 | pub fn try_eval_for_typeck( | |
193 | self, | |
194 | tcx: TyCtxt<'tcx>, | |
195 | param_env: ParamEnv<'tcx>, | |
196 | ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> { | |
197 | match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) { | |
198 | Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)), | |
199 | Some(Ok(EvalResult::ConstVal(_))) => unreachable!(), | |
200 | Some(Err(e)) => Some(Err(e)), | |
201 | None => None, | |
202 | } | |
203 | } | |
204 | ||
205 | #[inline] | |
206 | fn try_eval_inner( | |
207 | self, | |
208 | tcx: TyCtxt<'tcx>, | |
209 | param_env: ParamEnv<'tcx>, | |
210 | eval_mode: EvalMode, | |
211 | ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> { | |
f2b60f7d | 212 | assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); |
94222f64 | 213 | if let ConstKind::Unevaluated(unevaluated) = self { |
3dfed10e XL |
214 | use crate::mir::interpret::ErrorHandled; |
215 | ||
216 | // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` | |
217 | // also does later, but we want to do it before checking for | |
218 | // inference variables. | |
219 | // Note that we erase regions *before* calling `with_reveal_all_normalized`, | |
220 | // so that we don't try to invoke this query with | |
221 | // any region variables. | |
94222f64 | 222 | let param_env_and = tcx |
fc512014 | 223 | .erase_regions(param_env) |
3dfed10e | 224 | .with_reveal_all_normalized(tcx) |
94222f64 | 225 | .and(tcx.erase_regions(unevaluated)); |
3dfed10e XL |
226 | |
227 | // HACK(eddyb) when the query key would contain inference variables, | |
228 | // attempt using identity substs and `ParamEnv` instead, that will succeed | |
229 | // when the expression doesn't depend on any parameters. | |
230 | // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that | |
231 | // we can call `infcx.const_eval_resolve` which handles inference variables. | |
94222f64 | 232 | let param_env_and = if param_env_and.needs_infer() { |
2b03887a | 233 | tcx.param_env(unevaluated.def.did).and(ty::UnevaluatedConst { |
94222f64 | 234 | def: unevaluated.def, |
5099ac24 | 235 | substs: InternalSubsts::identity_for_item(tcx, unevaluated.def.did), |
94222f64 | 236 | }) |
3dfed10e | 237 | } else { |
94222f64 | 238 | param_env_and |
3dfed10e XL |
239 | }; |
240 | ||
241 | // FIXME(eddyb) maybe the `const_eval_*` methods should take | |
94222f64 XL |
242 | // `ty::ParamEnvAnd` instead of having them separate. |
243 | let (param_env, unevaluated) = param_env_and.into_parts(); | |
3dfed10e XL |
244 | // try to resolve e.g. associated constants to their definition on an impl, and then |
245 | // evaluate the const. | |
923072b8 FG |
246 | match eval_mode { |
247 | EvalMode::Typeck => { | |
248 | match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) { | |
249 | // NOTE(eddyb) `val` contains no lifetimes/types/consts, | |
250 | // and we use the original type, so nothing from `substs` | |
251 | // (which may be identity substs, see above), | |
252 | // can leak through `val` into the const we return. | |
253 | Ok(val) => Some(Ok(EvalResult::ValTree(val?))), | |
487cf647 | 254 | Err(ErrorHandled::TooGeneric) => None, |
923072b8 FG |
255 | Err(ErrorHandled::Reported(e)) => Some(Err(e)), |
256 | } | |
257 | } | |
258 | EvalMode::Mir => { | |
f2b60f7d | 259 | match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) { |
923072b8 FG |
260 | // NOTE(eddyb) `val` contains no lifetimes/types/consts, |
261 | // and we use the original type, so nothing from `substs` | |
262 | // (which may be identity substs, see above), | |
263 | // can leak through `val` into the const we return. | |
264 | Ok(val) => Some(Ok(EvalResult::ConstVal(val))), | |
487cf647 | 265 | Err(ErrorHandled::TooGeneric) => None, |
923072b8 FG |
266 | Err(ErrorHandled::Reported(e)) => Some(Err(e)), |
267 | } | |
268 | } | |
3dfed10e XL |
269 | } |
270 | } else { | |
271 | None | |
272 | } | |
273 | } | |
274 | } |