1 use std::convert::TryInto;
2 use std::fmt;
3 
4 use crate::mir::interpret::{AllocId, ConstValue, 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 /// An unevaluated, potentially generic, constant.
16 ///
17 /// If `substs_` is `None` it means that this anon const
18 /// still has its default substs.
19 ///
20 /// We check for all possible substs in `fn default_anon_const_substs`,
21 /// so refer to that check for more info.
22 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
23 #[derive(Hash, HashStable)]
24 pub struct Unevaluated<'tcx, P = Option<Promoted>> {
25     pub def: ty::WithOptConstParam<DefId>,
26     pub substs_: Option<SubstsRef<'tcx>>,
27     pub promoted: P,
28 }
29 
30 impl<'tcx> Unevaluated<'tcx> {
31     #[inline]
shrink(self) -> Unevaluated<'tcx, ()>32     pub fn shrink(self) -> Unevaluated<'tcx, ()> {
33         debug_assert_eq!(self.promoted, None);
34         Unevaluated { def: self.def, substs_: self.substs_, promoted: () }
35     }
36 }
37 
38 impl<'tcx> Unevaluated<'tcx, ()> {
39     #[inline]
expand(self) -> Unevaluated<'tcx>40     pub fn expand(self) -> Unevaluated<'tcx> {
41         Unevaluated { def: self.def, substs_: self.substs_, promoted: None }
42     }
43 }
44 
45 impl<'tcx, P: Default> Unevaluated<'tcx, P> {
46     #[inline]
new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P>47     pub fn new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P> {
48         Unevaluated { def, substs_: Some(substs), promoted: Default::default() }
49     }
50 }
51 
52 impl<'tcx, P: Default + PartialEq + fmt::Debug> Unevaluated<'tcx, P> {
53     #[inline]
substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx>54     pub fn substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx> {
55         self.substs_.unwrap_or_else(|| {
56             // We must not use the parents default substs for promoted constants
57             // as that can result in incorrect substs and calls the `default_anon_const_substs`
58             // for something that might not actually be a constant.
59             debug_assert_eq!(self.promoted, Default::default());
60             tcx.default_anon_const_substs(self.def.did)
61         })
62     }
63 }
64 
65 /// Represents a constant in Rust.
66 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
67 #[derive(Hash, HashStable)]
68 pub enum ConstKind<'tcx> {
69     /// A const generic parameter.
70     Param(ty::ParamConst),
71 
72     /// Infer the value of the const.
73     Infer(InferConst<'tcx>),
74 
75     /// Bound const variable, used only when preparing a trait query.
76     Bound(ty::DebruijnIndex, ty::BoundVar),
77 
78     /// A placeholder const - universally quantified higher-ranked const.
79     Placeholder(ty::PlaceholderConst<'tcx>),
80 
81     /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
82     /// variants when the code is monomorphic enough for that.
83     Unevaluated(Unevaluated<'tcx>),
84 
85     /// Used to hold computed value.
86     Value(ConstValue<'tcx>),
87 
88     /// A placeholder for a const which could not be computed; this is
89     /// propagated to avoid useless error messages.
90     Error(ty::DelaySpanBugEmitted),
91 }
92 
93 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
94 static_assert_size!(ConstKind<'_>, 40);
95 
96 impl<'tcx> ConstKind<'tcx> {
97     #[inline]
try_to_value(self) -> Option<ConstValue<'tcx>>98     pub fn try_to_value(self) -> Option<ConstValue<'tcx>> {
99         if let ConstKind::Value(val) = self { Some(val) } else { None }
100     }
101 
102     #[inline]
try_to_scalar(self) -> Option<Scalar<AllocId>>103     pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
104         self.try_to_value()?.try_to_scalar()
105     }
106 
107     #[inline]
try_to_scalar_int(self) -> Option<ScalarInt>108     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
109         Some(self.try_to_value()?.try_to_scalar()?.assert_int())
110     }
111 
112     #[inline]
try_to_bits(self, size: Size) -> Option<u128>113     pub fn try_to_bits(self, size: Size) -> Option<u128> {
114         self.try_to_scalar_int()?.to_bits(size).ok()
115     }
116 
117     #[inline]
try_to_bool(self) -> Option<bool>118     pub fn try_to_bool(self) -> Option<bool> {
119         self.try_to_scalar_int()?.try_into().ok()
120     }
121 
122     #[inline]
try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64>123     pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
124         self.try_to_value()?.try_to_machine_usize(tcx)
125     }
126 }
127 
128 /// An inference variable for a const, for use in const generics.
129 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
130 #[derive(HashStable)]
131 pub enum InferConst<'tcx> {
132     /// Infer the value of the const.
133     Var(ty::ConstVid<'tcx>),
134     /// A fresh const variable. See `infer::freshen` for more details.
135     Fresh(u32),
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.
eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self142     pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
143         self.try_eval(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`.
try_eval( self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ) -> Option<Result<ConstValue<'tcx>, ErrorReported>>149     pub(super) fn try_eval(
150         self,
151         tcx: TyCtxt<'tcx>,
152         param_env: ParamEnv<'tcx>,
153     ) -> Option<Result<ConstValue<'tcx>, ErrorReported>> {
154         if let ConstKind::Unevaluated(unevaluated) = self {
155             use crate::mir::interpret::ErrorHandled;
156 
157             // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
158             // also does later, but we want to do it before checking for
159             // inference variables.
160             // Note that we erase regions *before* calling `with_reveal_all_normalized`,
161             // so that we don't try to invoke this query with
162             // any region variables.
163             let param_env_and = tcx
164                 .erase_regions(param_env)
165                 .with_reveal_all_normalized(tcx)
166                 .and(tcx.erase_regions(unevaluated));
167 
168             // HACK(eddyb) when the query key would contain inference variables,
169             // attempt using identity substs and `ParamEnv` instead, that will succeed
170             // when the expression doesn't depend on any parameters.
171             // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
172             // we can call `infcx.const_eval_resolve` which handles inference variables.
173             let param_env_and = if param_env_and.needs_infer() {
174                 tcx.param_env(unevaluated.def.did).and(ty::Unevaluated {
175                     def: unevaluated.def,
176                     substs_: Some(InternalSubsts::identity_for_item(tcx, unevaluated.def.did)),
177                     promoted: unevaluated.promoted,
178                 })
179             } else {
180                 param_env_and
181             };
182 
183             // FIXME(eddyb) maybe the `const_eval_*` methods should take
184             // `ty::ParamEnvAnd` instead of having them separate.
185             let (param_env, unevaluated) = param_env_and.into_parts();
186             // try to resolve e.g. associated constants to their definition on an impl, and then
187             // evaluate the const.
188             match tcx.const_eval_resolve(param_env, unevaluated, None) {
189                 // NOTE(eddyb) `val` contains no lifetimes/types/consts,
190                 // and we use the original type, so nothing from `substs`
191                 // (which may be identity substs, see above),
192                 // can leak through `val` into the const we return.
193                 Ok(val) => Some(Ok(val)),
194                 Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None,
195                 Err(ErrorHandled::Reported(e)) => Some(Err(e)),
196             }
197         } else {
198             None
199         }
200     }
201 }
202