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