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