]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/consts/kind.rs
Rollup merge of #101365 - matthiaskrgr:clones2, r=fee1-dead
[rust.git] / compiler / rustc_middle / src / ty / consts / kind.rs
1 use std::convert::TryInto;
2
3 use crate::mir::interpret::{AllocId, ConstValue, Scalar};
4 use crate::mir::Promoted;
5 use crate::ty::subst::{InternalSubsts, SubstsRef};
6 use crate::ty::ParamEnv;
7 use crate::ty::{self, TyCtxt, TypeVisitable};
8 use rustc_errors::ErrorGuaranteed;
9 use rustc_hir::def_id::DefId;
10 use rustc_macros::HashStable;
11 use rustc_target::abi::Size;
12
13 use super::ScalarInt;
14 /// An unevaluated, potentially generic, constant.
15 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
16 #[derive(Hash, HashStable)]
17 pub struct Unevaluated<'tcx, P = Option<Promoted>> {
18     pub def: ty::WithOptConstParam<DefId>,
19     pub substs: SubstsRef<'tcx>,
20     pub promoted: P,
21 }
22
23 impl rustc_errors::IntoDiagnosticArg for Unevaluated<'_> {
24     fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
25         format!("{:?}", self).into_diagnostic_arg()
26     }
27 }
28
29 impl<'tcx> Unevaluated<'tcx> {
30     #[inline]
31     pub fn shrink(self) -> Unevaluated<'tcx, ()> {
32         debug_assert_eq!(self.promoted, None);
33         Unevaluated { def: self.def, substs: self.substs, promoted: () }
34     }
35 }
36
37 impl<'tcx> Unevaluated<'tcx, ()> {
38     #[inline]
39     pub fn expand(self) -> Unevaluated<'tcx> {
40         Unevaluated { def: self.def, substs: self.substs, promoted: None }
41     }
42 }
43
44 impl<'tcx, P: Default> Unevaluated<'tcx, P> {
45     #[inline]
46     pub fn new(def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P> {
47         Unevaluated { def, substs, promoted: Default::default() }
48     }
49 }
50
51 /// Represents a constant in Rust.
52 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
53 #[derive(Hash, HashStable)]
54 pub enum ConstKind<'tcx> {
55     /// A const generic parameter.
56     Param(ty::ParamConst),
57
58     /// Infer the value of the const.
59     Infer(InferConst<'tcx>),
60
61     /// Bound const variable, used only when preparing a trait query.
62     Bound(ty::DebruijnIndex, ty::BoundVar),
63
64     /// A placeholder const - universally quantified higher-ranked const.
65     Placeholder(ty::PlaceholderConst<'tcx>),
66
67     /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
68     /// variants when the code is monomorphic enough for that.
69     Unevaluated(Unevaluated<'tcx>),
70
71     /// Used to hold computed value.
72     Value(ty::ValTree<'tcx>),
73
74     /// A placeholder for a const which could not be computed; this is
75     /// propagated to avoid useless error messages.
76     Error(ty::DelaySpanBugEmitted),
77 }
78
79 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
80 static_assert_size!(ConstKind<'_>, 40);
81
82 impl<'tcx> ConstKind<'tcx> {
83     #[inline]
84     pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
85         if let ConstKind::Value(val) = self { Some(val) } else { None }
86     }
87
88     #[inline]
89     pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
90         self.try_to_value()?.try_to_scalar()
91     }
92
93     #[inline]
94     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
95         self.try_to_value()?.try_to_scalar_int()
96     }
97
98     #[inline]
99     pub fn try_to_bits(self, size: Size) -> Option<u128> {
100         self.try_to_scalar_int()?.to_bits(size).ok()
101     }
102
103     #[inline]
104     pub fn try_to_bool(self) -> Option<bool> {
105         self.try_to_scalar_int()?.try_into().ok()
106     }
107
108     #[inline]
109     pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
110         self.try_to_value()?.try_to_machine_usize(tcx)
111     }
112 }
113
114 /// An inference variable for a const, for use in const generics.
115 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
116 #[derive(HashStable)]
117 pub enum InferConst<'tcx> {
118     /// Infer the value of the const.
119     Var(ty::ConstVid<'tcx>),
120     /// A fresh const variable. See `infer::freshen` for more details.
121     Fresh(u32),
122 }
123
124 enum EvalMode {
125     Typeck,
126     Mir,
127 }
128
129 enum EvalResult<'tcx> {
130     ValTree(ty::ValTree<'tcx>),
131     ConstVal(ConstValue<'tcx>),
132 }
133
134 impl<'tcx> ConstKind<'tcx> {
135     #[inline]
136     /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
137     /// unevaluated constant.
138     pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
139         self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value)
140     }
141
142     #[inline]
143     /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
144     /// return `None`.
145     // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
146     pub fn try_eval_for_mir(
147         self,
148         tcx: TyCtxt<'tcx>,
149         param_env: ParamEnv<'tcx>,
150     ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
151         match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
152             Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
153             Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
154             Some(Err(e)) => Some(Err(e)),
155             None => None,
156         }
157     }
158
159     #[inline]
160     /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
161     /// return `None`.
162     // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
163     pub fn try_eval_for_typeck(
164         self,
165         tcx: TyCtxt<'tcx>,
166         param_env: ParamEnv<'tcx>,
167     ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
168         match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
169             Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
170             Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
171             Some(Err(e)) => Some(Err(e)),
172             None => None,
173         }
174     }
175
176     #[inline]
177     fn try_eval_inner(
178         self,
179         tcx: TyCtxt<'tcx>,
180         param_env: ParamEnv<'tcx>,
181         eval_mode: EvalMode,
182     ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
183         if let ConstKind::Unevaluated(unevaluated) = self {
184             use crate::mir::interpret::ErrorHandled;
185
186             // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
187             // also does later, but we want to do it before checking for
188             // inference variables.
189             // Note that we erase regions *before* calling `with_reveal_all_normalized`,
190             // so that we don't try to invoke this query with
191             // any region variables.
192             let param_env_and = tcx
193                 .erase_regions(param_env)
194                 .with_reveal_all_normalized(tcx)
195                 .and(tcx.erase_regions(unevaluated));
196
197             // HACK(eddyb) when the query key would contain inference variables,
198             // attempt using identity substs and `ParamEnv` instead, that will succeed
199             // when the expression doesn't depend on any parameters.
200             // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
201             // we can call `infcx.const_eval_resolve` which handles inference variables.
202             let param_env_and = if param_env_and.needs_infer() {
203                 tcx.param_env(unevaluated.def.did).and(ty::Unevaluated {
204                     def: unevaluated.def,
205                     substs: InternalSubsts::identity_for_item(tcx, unevaluated.def.did),
206                     promoted: unevaluated.promoted,
207                 })
208             } else {
209                 param_env_and
210             };
211
212             // FIXME(eddyb) maybe the `const_eval_*` methods should take
213             // `ty::ParamEnvAnd` instead of having them separate.
214             let (param_env, unevaluated) = param_env_and.into_parts();
215             // try to resolve e.g. associated constants to their definition on an impl, and then
216             // evaluate the const.
217             match eval_mode {
218                 EvalMode::Typeck => {
219                     match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
220                         // NOTE(eddyb) `val` contains no lifetimes/types/consts,
221                         // and we use the original type, so nothing from `substs`
222                         // (which may be identity substs, see above),
223                         // can leak through `val` into the const we return.
224                         Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
225                         Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None,
226                         Err(ErrorHandled::Reported(e)) => Some(Err(e)),
227                     }
228                 }
229                 EvalMode::Mir => {
230                     match tcx.const_eval_resolve(param_env, unevaluated, None) {
231                         // NOTE(eddyb) `val` contains no lifetimes/types/consts,
232                         // and we use the original type, so nothing from `substs`
233                         // (which may be identity substs, see above),
234                         // can leak through `val` into the const we return.
235                         Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
236                         Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None,
237                         Err(ErrorHandled::Reported(e)) => Some(Err(e)),
238                     }
239                 }
240             }
241         } else {
242             None
243         }
244     }
245 }