]>
Commit | Line | Data |
---|---|---|
3dfed10e XL |
1 | use crate::mir::interpret::ConstValue; |
2 | use crate::mir::interpret::{LitToConstInput, Scalar}; | |
3c0e092e XL |
3 | use crate::ty::{ |
4 | self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, | |
5 | TyCtxt, TypeFoldable, | |
6 | }; | |
5099ac24 | 7 | use rustc_data_structures::intern::Interned; |
3dfed10e XL |
8 | use rustc_errors::ErrorReported; |
9 | use rustc_hir as hir; | |
cdc7bbd5 | 10 | use rustc_hir::def_id::{DefId, LocalDefId}; |
3dfed10e | 11 | use rustc_macros::HashStable; |
5099ac24 | 12 | use std::fmt; |
3dfed10e XL |
13 | |
14 | mod int; | |
15 | mod kind; | |
6a06907d | 16 | mod valtree; |
3dfed10e XL |
17 | |
18 | pub use int::*; | |
19 | pub use kind::*; | |
6a06907d | 20 | pub use valtree::*; |
3dfed10e | 21 | |
5099ac24 FG |
22 | /// Use this rather than `ConstS`, whenever possible. |
23 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] | |
24 | #[cfg_attr(not(bootstrap), rustc_pass_by_value)] | |
25 | pub struct Const<'tcx>(pub Interned<'tcx, ConstS<'tcx>>); | |
26 | ||
27 | impl<'tcx> fmt::Debug for Const<'tcx> { | |
28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
29 | // This reflects what `Const` looked liked before `Interned` was | |
30 | // introduced. We print it like this to avoid having to update expected | |
31 | // output in a lot of tests. | |
32 | write!(f, "Const {{ ty: {:?}, val: {:?} }}", self.ty(), self.val()) | |
33 | } | |
34 | } | |
35 | ||
3dfed10e | 36 | /// Typed constant value. |
5099ac24 FG |
37 | #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, HashStable, TyEncodable, TyDecodable)] |
38 | pub struct ConstS<'tcx> { | |
3dfed10e | 39 | pub ty: Ty<'tcx>, |
3dfed10e | 40 | pub val: ConstKind<'tcx>, |
f035d41b XL |
41 | } |
42 | ||
6a06907d | 43 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
5099ac24 | 44 | static_assert_size!(ConstS<'_>, 48); |
3dfed10e XL |
45 | |
46 | impl<'tcx> Const<'tcx> { | |
5099ac24 FG |
47 | pub fn ty(self) -> Ty<'tcx> { |
48 | self.0.ty | |
49 | } | |
50 | ||
51 | pub fn val(self) -> ConstKind<'tcx> { | |
52 | self.0.val | |
53 | } | |
54 | ||
3dfed10e XL |
55 | /// Literals and const generic parameters are eagerly converted to a constant, everything else |
56 | /// becomes `Unevaluated`. | |
5099ac24 | 57 | pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { |
3dfed10e | 58 | Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id)) |
f035d41b | 59 | } |
f035d41b | 60 | |
5099ac24 | 61 | #[instrument(skip(tcx), level = "debug")] |
3dfed10e XL |
62 | pub fn from_opt_const_arg_anon_const( |
63 | tcx: TyCtxt<'tcx>, | |
64 | def: ty::WithOptConstParam<LocalDefId>, | |
5099ac24 | 65 | ) -> Self { |
3dfed10e XL |
66 | debug!("Const::from_anon_const(def={:?})", def); |
67 | ||
5099ac24 | 68 | let body_id = match tcx.hir().get_by_def_id(def.did) { |
3dfed10e XL |
69 | hir::Node::AnonConst(ac) => ac.body, |
70 | _ => span_bug!( | |
71 | tcx.def_span(def.did.to_def_id()), | |
72 | "from_anon_const can only process anonymous constants" | |
73 | ), | |
74 | }; | |
75 | ||
76 | let expr = &tcx.hir().body(body_id).value; | |
5099ac24 | 77 | debug!(?expr); |
3dfed10e XL |
78 | |
79 | let ty = tcx.type_of(def.def_id_for_type_of()); | |
80 | ||
3c0e092e XL |
81 | match Self::try_eval_lit_or_param(tcx, ty, expr) { |
82 | Some(v) => v, | |
5099ac24 | 83 | None => tcx.mk_const(ty::ConstS { |
3c0e092e XL |
84 | val: ty::ConstKind::Unevaluated(ty::Unevaluated { |
85 | def: def.to_global(), | |
5099ac24 | 86 | substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), |
3c0e092e XL |
87 | promoted: None, |
88 | }), | |
89 | ty, | |
90 | }), | |
91 | } | |
92 | } | |
93 | ||
5099ac24 | 94 | #[instrument(skip(tcx), level = "debug")] |
3c0e092e XL |
95 | fn try_eval_lit_or_param( |
96 | tcx: TyCtxt<'tcx>, | |
97 | ty: Ty<'tcx>, | |
98 | expr: &'tcx hir::Expr<'tcx>, | |
5099ac24 FG |
99 | ) -> Option<Self> { |
100 | // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments | |
101 | // currently have to be wrapped in curly brackets, so it's necessary to special-case. | |
102 | let expr = match &expr.kind { | |
103 | hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { | |
104 | block.expr.as_ref().unwrap() | |
105 | } | |
106 | _ => expr, | |
107 | }; | |
108 | ||
3dfed10e XL |
109 | let lit_input = match expr.kind { |
110 | hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), | |
6a06907d | 111 | hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind { |
3dfed10e XL |
112 | hir::ExprKind::Lit(ref lit) => { |
113 | Some(LitToConstInput { lit: &lit.node, ty, neg: true }) | |
f035d41b | 114 | } |
3dfed10e XL |
115 | _ => None, |
116 | }, | |
117 | _ => None, | |
118 | }; | |
119 | ||
120 | if let Some(lit_input) = lit_input { | |
121 | // If an error occurred, ignore that it's a literal and leave reporting the error up to | |
122 | // mir. | |
5099ac24 FG |
123 | match tcx.at(expr.span).lit_to_const(lit_input) { |
124 | Ok(c) => return Some(c), | |
125 | Err(e) => { | |
126 | tcx.sess.delay_span_bug( | |
127 | expr.span, | |
128 | &format!("Const::from_anon_const: couldn't lit_to_const {:?}", e), | |
129 | ); | |
130 | } | |
f035d41b | 131 | } |
3dfed10e XL |
132 | } |
133 | ||
3dfed10e | 134 | use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; |
3c0e092e | 135 | match expr.kind { |
3dfed10e XL |
136 | ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { |
137 | // Find the name and index of the const parameter by indexing the generics of | |
138 | // the parent item and construct a `ParamConst`. | |
139 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); | |
140 | let item_id = tcx.hir().get_parent_node(hir_id); | |
141 | let item_def_id = tcx.hir().local_def_id(item_id); | |
142 | let generics = tcx.generics_of(item_def_id.to_def_id()); | |
5869c6ff | 143 | let index = generics.param_def_id_to_index[&def_id]; |
3dfed10e | 144 | let name = tcx.hir().name(hir_id); |
5099ac24 | 145 | Some(tcx.mk_const(ty::ConstS { |
3c0e092e XL |
146 | val: ty::ConstKind::Param(ty::ParamConst::new(index, name)), |
147 | ty, | |
148 | })) | |
f035d41b | 149 | } |
3c0e092e XL |
150 | _ => None, |
151 | } | |
152 | } | |
153 | ||
5099ac24 | 154 | pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { |
3c0e092e XL |
155 | debug!("Const::from_inline_const(def_id={:?})", def_id); |
156 | ||
157 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); | |
158 | ||
159 | let body_id = match tcx.hir().get(hir_id) { | |
160 | hir::Node::AnonConst(ac) => ac.body, | |
161 | _ => span_bug!( | |
162 | tcx.def_span(def_id.to_def_id()), | |
163 | "from_inline_const can only process anonymous constants" | |
164 | ), | |
3dfed10e XL |
165 | }; |
166 | ||
3c0e092e XL |
167 | let expr = &tcx.hir().body(body_id).value; |
168 | ||
169 | let ty = tcx.typeck(def_id).node_type(hir_id); | |
170 | ||
171 | let ret = match Self::try_eval_lit_or_param(tcx, ty, expr) { | |
172 | Some(v) => v, | |
173 | None => { | |
174 | let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); | |
175 | let parent_substs = | |
176 | tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); | |
177 | let substs = | |
178 | InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) | |
179 | .substs; | |
5099ac24 | 180 | tcx.mk_const(ty::ConstS { |
3c0e092e XL |
181 | val: ty::ConstKind::Unevaluated(ty::Unevaluated { |
182 | def: ty::WithOptConstParam::unknown(def_id).to_global(), | |
5099ac24 | 183 | substs, |
3c0e092e XL |
184 | promoted: None, |
185 | }), | |
186 | ty, | |
187 | }) | |
188 | } | |
189 | }; | |
5099ac24 | 190 | debug_assert!(!ret.has_free_regions()); |
3c0e092e | 191 | ret |
3dfed10e XL |
192 | } |
193 | ||
3dfed10e | 194 | /// Interns the given value as a constant. |
cdc7bbd5 | 195 | #[inline] |
5099ac24 FG |
196 | pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self { |
197 | tcx.mk_const(ConstS { val: ConstKind::Value(val), ty }) | |
3dfed10e XL |
198 | } |
199 | ||
200 | #[inline] | |
201 | /// Interns the given scalar as a constant. | |
5099ac24 | 202 | pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> Self { |
3dfed10e XL |
203 | Self::from_value(tcx, ConstValue::Scalar(val), ty) |
204 | } | |
205 | ||
206 | #[inline] | |
207 | /// Creates a constant with the given integer value and interns it. | |
5099ac24 | 208 | pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> Self { |
3dfed10e XL |
209 | let size = tcx |
210 | .layout_of(ty) | |
211 | .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) | |
212 | .size; | |
213 | Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) | |
214 | } | |
215 | ||
216 | #[inline] | |
217 | /// Creates an interned zst constant. | |
5099ac24 | 218 | pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { |
29967ef6 | 219 | Self::from_scalar(tcx, Scalar::ZST, ty) |
3dfed10e XL |
220 | } |
221 | ||
222 | #[inline] | |
223 | /// Creates an interned bool constant. | |
5099ac24 | 224 | pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self { |
3dfed10e XL |
225 | Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) |
226 | } | |
227 | ||
228 | #[inline] | |
229 | /// Creates an interned usize constant. | |
5099ac24 | 230 | pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self { |
3dfed10e XL |
231 | Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) |
232 | } | |
233 | ||
234 | #[inline] | |
235 | /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of | |
236 | /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it | |
237 | /// contains const generic parameters or pointers). | |
238 | pub fn try_eval_bits( | |
5099ac24 | 239 | self, |
3dfed10e XL |
240 | tcx: TyCtxt<'tcx>, |
241 | param_env: ParamEnv<'tcx>, | |
242 | ty: Ty<'tcx>, | |
243 | ) -> Option<u128> { | |
5099ac24 | 244 | assert_eq!(self.ty(), ty); |
3dfed10e XL |
245 | let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; |
246 | // if `ty` does not depend on generic parameters, use an empty param_env | |
5099ac24 | 247 | self.val().eval(tcx, param_env).try_to_bits(size) |
3dfed10e XL |
248 | } |
249 | ||
250 | #[inline] | |
5099ac24 FG |
251 | pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> { |
252 | self.val().eval(tcx, param_env).try_to_bool() | |
3dfed10e XL |
253 | } |
254 | ||
255 | #[inline] | |
5099ac24 FG |
256 | pub fn try_eval_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> { |
257 | self.val().eval(tcx, param_env).try_to_machine_usize(tcx) | |
3dfed10e XL |
258 | } |
259 | ||
260 | #[inline] | |
261 | /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the | |
262 | /// unevaluated constant. | |
5099ac24 FG |
263 | pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> { |
264 | if let Some(val) = self.val().try_eval(tcx, param_env) { | |
3dfed10e | 265 | match val { |
5099ac24 FG |
266 | Ok(val) => Const::from_value(tcx, val, self.ty()), |
267 | Err(ErrorReported) => tcx.const_error(self.ty()), | |
3dfed10e XL |
268 | } |
269 | } else { | |
270 | self | |
f035d41b XL |
271 | } |
272 | } | |
3dfed10e XL |
273 | |
274 | #[inline] | |
275 | /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. | |
5099ac24 | 276 | pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { |
3dfed10e XL |
277 | self.try_eval_bits(tcx, param_env, ty) |
278 | .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) | |
279 | } | |
280 | ||
281 | #[inline] | |
282 | /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. | |
5099ac24 | 283 | pub fn eval_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { |
3dfed10e XL |
284 | self.try_eval_usize(tcx, param_env) |
285 | .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) | |
286 | } | |
f035d41b | 287 | } |
cdc7bbd5 | 288 | |
5099ac24 FG |
289 | pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Const<'tcx> { |
290 | let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) { | |
cdc7bbd5 XL |
291 | hir::Node::GenericParam(hir::GenericParam { |
292 | kind: hir::GenericParamKind::Const { ty: _, default: Some(ac) }, | |
293 | .. | |
294 | }) => tcx.hir().local_def_id(ac.hir_id), | |
295 | _ => span_bug!( | |
296 | tcx.def_span(def_id), | |
297 | "`const_param_default` expected a generic parameter with a constant" | |
298 | ), | |
299 | }; | |
300 | Const::from_anon_const(tcx, default_def_id) | |
301 | } |