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