]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/consts/kind.rs
Rollup merge of #104895 - chenyukang:yukang/fix-104884-serde, r=TaKO8Ki
[rust.git] / compiler / rustc_middle / src / ty / consts / kind.rs
1 use std::convert::TryInto;
2
3 use super::Const;
4 use crate::mir;
5 use crate::mir::interpret::{AllocId, ConstValue, Scalar};
6 use crate::ty::abstract_const::CastKind;
7 use crate::ty::subst::{InternalSubsts, SubstsRef};
8 use crate::ty::ParamEnv;
9 use crate::ty::{self, List, Ty, TyCtxt, TypeVisitable};
10 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
11 use rustc_errors::ErrorGuaranteed;
12 use rustc_hir::def_id::DefId;
13 use rustc_macros::HashStable;
14 use rustc_target::abi::Size;
15
16 use super::ScalarInt;
17
18 /// An unevaluated (potentially generic) constant used in the type-system.
19 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
20 #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
21 pub struct UnevaluatedConst<'tcx> {
22     pub def: ty::WithOptConstParam<DefId>,
23     pub substs: SubstsRef<'tcx>,
24 }
25
26 impl rustc_errors::IntoDiagnosticArg for UnevaluatedConst<'_> {
27     fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
28         format!("{:?}", self).into_diagnostic_arg()
29     }
30 }
31
32 impl<'tcx> UnevaluatedConst<'tcx> {
33     #[inline]
34     pub fn expand(self) -> mir::UnevaluatedConst<'tcx> {
35         mir::UnevaluatedConst { def: self.def, substs: self.substs, promoted: None }
36     }
37 }
38
39 impl<'tcx> UnevaluatedConst<'tcx> {
40     #[inline]
41     pub fn new(
42         def: ty::WithOptConstParam<DefId>,
43         substs: SubstsRef<'tcx>,
44     ) -> UnevaluatedConst<'tcx> {
45         UnevaluatedConst { def, substs }
46     }
47 }
48
49 /// Represents a constant in Rust.
50 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
51 #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
52 #[derive(derive_more::From)]
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.
64     Placeholder(ty::PlaceholderConst<'tcx>),
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.
68     Unevaluated(UnevaluatedConst<'tcx>),
69
70     /// Used to hold computed value.
71     Value(ty::ValTree<'tcx>),
72
73     /// A placeholder for a const which could not be computed; this is
74     /// propagated to avoid useless error messages.
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     }
86 }
87
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
100 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
101 static_assert_size!(ConstKind<'_>, 32);
102
103 impl<'tcx> ConstKind<'tcx> {
104     #[inline]
105     pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
106         if let ConstKind::Value(val) = self { Some(val) } else { None }
107     }
108
109     #[inline]
110     pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
111         self.try_to_value()?.try_to_scalar()
112     }
113
114     #[inline]
115     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
116         self.try_to_value()?.try_to_scalar_int()
117     }
118
119     #[inline]
120     pub fn try_to_bits(self, size: Size) -> Option<u128> {
121         self.try_to_scalar_int()?.to_bits(size).ok()
122     }
123
124     #[inline]
125     pub fn try_to_bool(self) -> Option<bool> {
126         self.try_to_scalar_int()?.try_into().ok()
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)]
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
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
153 enum EvalMode {
154     Typeck,
155     Mir,
156 }
157
158 enum EvalResult<'tcx> {
159     ValTree(ty::ValTree<'tcx>),
160     ConstVal(ConstValue<'tcx>),
161 }
162
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 {
168         self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value)
169     }
170
171     #[inline]
172     /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
173     /// return `None`.
174     // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
175     pub fn try_eval_for_mir(
176         self,
177         tcx: TyCtxt<'tcx>,
178         param_env: ParamEnv<'tcx>,
179     ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
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>> {
212         assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
213         if let ConstKind::Unevaluated(unevaluated) = self {
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.
222             let param_env_and = tcx
223                 .erase_regions(param_env)
224                 .with_reveal_all_normalized(tcx)
225                 .and(tcx.erase_regions(unevaluated));
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.
232             let param_env_and = if param_env_and.needs_infer() {
233                 tcx.param_env(unevaluated.def.did).and(ty::UnevaluatedConst {
234                     def: unevaluated.def,
235                     substs: InternalSubsts::identity_for_item(tcx, unevaluated.def.did),
236                 })
237             } else {
238                 param_env_and
239             };
240
241             // FIXME(eddyb) maybe the `const_eval_*` methods should take
242             // `ty::ParamEnvAnd` instead of having them separate.
243             let (param_env, unevaluated) = param_env_and.into_parts();
244             // try to resolve e.g. associated constants to their definition on an impl, and then
245             // evaluate the const.
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?))),
254                         Err(ErrorHandled::TooGeneric) => None,
255                         Err(ErrorHandled::Reported(e)) => Some(Err(e)),
256                     }
257                 }
258                 EvalMode::Mir => {
259                     match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
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))),
265                         Err(ErrorHandled::TooGeneric) => None,
266                         Err(ErrorHandled::Reported(e)) => Some(Err(e)),
267                     }
268                 }
269             }
270         } else {
271             None
272         }
273     }
274 }