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