]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/consts/kind.rs
Perform 'deep recollection' in test helper macros
[rust.git] / compiler / rustc_middle / src / ty / consts / kind.rs
1 use std::convert::TryInto;
2
3 use crate::mir::interpret::ConstValue;
4 use crate::mir::interpret::Scalar;
5 use crate::mir::Promoted;
6 use crate::ty::subst::{InternalSubsts, SubstsRef};
7 use crate::ty::ParamEnv;
8 use crate::ty::{self, TyCtxt, TypeFoldable};
9 use rustc_errors::ErrorReported;
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 /// Represents a constant in Rust.
17 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
18 #[derive(HashStable)]
19 pub enum ConstKind<'tcx> {
20     /// A const generic parameter.
21     Param(ty::ParamConst),
22
23     /// Infer the value of the const.
24     Infer(InferConst<'tcx>),
25
26     /// Bound const variable, used only when preparing a trait query.
27     Bound(ty::DebruijnIndex, ty::BoundVar),
28
29     /// A placeholder const - universally quantified higher-ranked const.
30     Placeholder(ty::PlaceholderConst<'tcx>),
31
32     /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
33     /// variants when the code is monomorphic enough for that.
34     Unevaluated(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>, Option<Promoted>),
35
36     /// Used to hold computed value.
37     Value(ConstValue<'tcx>),
38
39     /// A placeholder for a const which could not be computed; this is
40     /// propagated to avoid useless error messages.
41     Error(ty::DelaySpanBugEmitted),
42 }
43
44 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
45 static_assert_size!(ConstKind<'_>, 40);
46
47 impl<'tcx> ConstKind<'tcx> {
48     #[inline]
49     pub fn try_to_value(self) -> Option<ConstValue<'tcx>> {
50         if let ConstKind::Value(val) = self { Some(val) } else { None }
51     }
52
53     #[inline]
54     pub fn try_to_scalar(self) -> Option<Scalar> {
55         self.try_to_value()?.try_to_scalar()
56     }
57
58     #[inline]
59     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
60         Some(self.try_to_value()?.try_to_scalar()?.assert_int())
61     }
62
63     #[inline]
64     pub fn try_to_bits(self, size: Size) -> Option<u128> {
65         self.try_to_scalar_int()?.to_bits(size).ok()
66     }
67
68     #[inline]
69     pub fn try_to_bool(self) -> Option<bool> {
70         self.try_to_scalar_int()?.try_into().ok()
71     }
72
73     #[inline]
74     pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
75         self.try_to_value()?.try_to_machine_usize(tcx)
76     }
77 }
78
79 /// An inference variable for a const, for use in const generics.
80 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
81 #[derive(HashStable)]
82 pub enum InferConst<'tcx> {
83     /// Infer the value of the const.
84     Var(ty::ConstVid<'tcx>),
85     /// A fresh const variable. See `infer::freshen` for more details.
86     Fresh(u32),
87 }
88
89 impl<'tcx> ConstKind<'tcx> {
90     #[inline]
91     /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
92     /// unevaluated constant.
93     pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
94         self.try_eval(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value)
95     }
96
97     #[inline]
98     /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
99     /// return `None`.
100     pub(super) fn try_eval(
101         self,
102         tcx: TyCtxt<'tcx>,
103         param_env: ParamEnv<'tcx>,
104     ) -> Option<Result<ConstValue<'tcx>, ErrorReported>> {
105         if let ConstKind::Unevaluated(def, substs, promoted) = self {
106             use crate::mir::interpret::ErrorHandled;
107
108             // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
109             // also does later, but we want to do it before checking for
110             // inference variables.
111             // Note that we erase regions *before* calling `with_reveal_all_normalized`,
112             // so that we don't try to invoke this query with
113             // any region variables.
114             let param_env_and_substs = tcx
115                 .erase_regions(param_env)
116                 .with_reveal_all_normalized(tcx)
117                 .and(tcx.erase_regions(substs));
118
119             // HACK(eddyb) when the query key would contain inference variables,
120             // attempt using identity substs and `ParamEnv` instead, that will succeed
121             // when the expression doesn't depend on any parameters.
122             // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
123             // we can call `infcx.const_eval_resolve` which handles inference variables.
124             let param_env_and_substs = if param_env_and_substs.needs_infer() {
125                 tcx.param_env(def.did).and(InternalSubsts::identity_for_item(tcx, def.did))
126             } else {
127                 param_env_and_substs
128             };
129
130             // FIXME(eddyb) maybe the `const_eval_*` methods should take
131             // `ty::ParamEnvAnd<SubstsRef>` instead of having them separate.
132             let (param_env, substs) = param_env_and_substs.into_parts();
133             // try to resolve e.g. associated constants to their definition on an impl, and then
134             // evaluate the const.
135             match tcx.const_eval_resolve(param_env, def, substs, promoted, None) {
136                 // NOTE(eddyb) `val` contains no lifetimes/types/consts,
137                 // and we use the original type, so nothing from `substs`
138                 // (which may be identity substs, see above),
139                 // can leak through `val` into the const we return.
140                 Ok(val) => Some(Ok(val)),
141                 Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None,
142                 Err(ErrorHandled::Reported(e)) => Some(Err(e)),
143             }
144         } else {
145             None
146         }
147     }
148 }