]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | //! Checking that constant values used in types can be successfully evaluated. |
2 | //! | |
3 | //! For concrete constants, this is fairly simple as we can just try and evaluate it. | |
4 | //! | |
5 | //! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`, | |
6 | //! this is not as easy. | |
7 | //! | |
8 | //! In this case we try to build an abstract representation of this constant using | |
c295e0f8 | 9 | //! `thir_abstract_const` which can then be checked for structural equality with other |
1b1a35ee | 10 | //! generic constants mentioned in the `caller_bounds` of the current environment. |
5099ac24 | 11 | use rustc_data_structures::intern::Interned; |
ee023bcb | 12 | use rustc_errors::ErrorGuaranteed; |
1b1a35ee | 13 | use rustc_hir::def::DefKind; |
1b1a35ee XL |
14 | use rustc_index::vec::IndexVec; |
15 | use rustc_infer::infer::InferCtxt; | |
c295e0f8 | 16 | use rustc_middle::mir; |
ee023bcb FG |
17 | use rustc_middle::mir::interpret::{ |
18 | ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar, | |
19 | }; | |
c295e0f8 XL |
20 | use rustc_middle::thir; |
21 | use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; | |
5869c6ff | 22 | use rustc_middle::ty::subst::{Subst, SubstsRef}; |
ee023bcb | 23 | use rustc_middle::ty::{self, DelaySpanBugEmitted, TyCtxt, TypeFoldable}; |
1b1a35ee | 24 | use rustc_session::lint; |
94222f64 | 25 | use rustc_span::def_id::LocalDefId; |
1b1a35ee XL |
26 | use rustc_span::Span; |
27 | ||
28 | use std::cmp; | |
cdc7bbd5 | 29 | use std::iter; |
29967ef6 | 30 | use std::ops::ControlFlow; |
1b1a35ee XL |
31 | |
32 | /// Check if a given constant can be evaluated. | |
ee023bcb | 33 | #[instrument(skip(infcx), level = "debug")] |
1b1a35ee XL |
34 | pub fn is_const_evaluatable<'cx, 'tcx>( |
35 | infcx: &InferCtxt<'cx, 'tcx>, | |
94222f64 | 36 | uv: ty::Unevaluated<'tcx, ()>, |
1b1a35ee XL |
37 | param_env: ty::ParamEnv<'tcx>, |
38 | span: Span, | |
cdc7bbd5 | 39 | ) -> Result<(), NotConstEvaluatable> { |
ee023bcb FG |
40 | let tcx = infcx.tcx; |
41 | ||
42 | if tcx.features().generic_const_exprs { | |
94222f64 | 43 | match AbstractConst::new(tcx, uv)? { |
1b1a35ee XL |
44 | // We are looking at a generic abstract constant. |
45 | Some(ct) => { | |
ee023bcb FG |
46 | if satisfied_from_param_env(tcx, ct, param_env)? { |
47 | return Ok(()); | |
1b1a35ee XL |
48 | } |
49 | ||
50 | // We were unable to unify the abstract constant with | |
51 | // a constant found in the caller bounds, there are | |
52 | // now three possible cases here. | |
1b1a35ee XL |
53 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] |
54 | enum FailureKind { | |
6a06907d XL |
55 | /// The abstract const still references an inference |
56 | /// variable, in this case we return `TooGeneric`. | |
1b1a35ee | 57 | MentionsInfer, |
6a06907d XL |
58 | /// The abstract const references a generic parameter, |
59 | /// this means that we emit an error here. | |
1b1a35ee | 60 | MentionsParam, |
6a06907d XL |
61 | /// The substs are concrete enough that we can simply |
62 | /// try and evaluate the given constant. | |
1b1a35ee XL |
63 | Concrete, |
64 | } | |
65 | let mut failure_kind = FailureKind::Concrete; | |
3c0e092e | 66 | walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) { |
1b1a35ee | 67 | Node::Leaf(leaf) => { |
1b1a35ee XL |
68 | if leaf.has_infer_types_or_consts() { |
69 | failure_kind = FailureKind::MentionsInfer; | |
5099ac24 | 70 | } else if leaf.has_param_types_or_consts() { |
1b1a35ee XL |
71 | failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); |
72 | } | |
29967ef6 XL |
73 | |
74 | ControlFlow::CONTINUE | |
75 | } | |
136023e0 | 76 | Node::Cast(_, _, ty) => { |
136023e0 XL |
77 | if ty.has_infer_types_or_consts() { |
78 | failure_kind = FailureKind::MentionsInfer; | |
5099ac24 | 79 | } else if ty.has_param_types_or_consts() { |
136023e0 XL |
80 | failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); |
81 | } | |
82 | ||
83 | ControlFlow::CONTINUE | |
84 | } | |
29967ef6 XL |
85 | Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { |
86 | ControlFlow::CONTINUE | |
1b1a35ee | 87 | } |
1b1a35ee XL |
88 | }); |
89 | ||
90 | match failure_kind { | |
91 | FailureKind::MentionsInfer => { | |
cdc7bbd5 | 92 | return Err(NotConstEvaluatable::MentionsInfer); |
1b1a35ee XL |
93 | } |
94 | FailureKind::MentionsParam => { | |
cdc7bbd5 | 95 | return Err(NotConstEvaluatable::MentionsParam); |
1b1a35ee XL |
96 | } |
97 | FailureKind::Concrete => { | |
98 | // Dealt with below by the same code which handles this | |
99 | // without the feature gate. | |
100 | } | |
101 | } | |
102 | } | |
103 | None => { | |
104 | // If we are dealing with a concrete constant, we can | |
105 | // reuse the old code path and try to evaluate | |
106 | // the constant. | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | let future_compat_lint = || { | |
94222f64 | 112 | if let Some(local_def_id) = uv.def.did.as_local() { |
1b1a35ee XL |
113 | infcx.tcx.struct_span_lint_hir( |
114 | lint::builtin::CONST_EVALUATABLE_UNCHECKED, | |
115 | infcx.tcx.hir().local_def_id_to_hir_id(local_def_id), | |
116 | span, | |
117 | |err| { | |
118 | err.build("cannot use constants which depend on generic parameters in types") | |
119 | .emit(); | |
120 | }, | |
121 | ); | |
122 | } | |
123 | }; | |
124 | ||
125 | // FIXME: We should only try to evaluate a given constant here if it is fully concrete | |
126 | // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. | |
127 | // | |
128 | // We previously did not check this, so we only emit a future compat warning if | |
129 | // const evaluation succeeds and the given constant is still polymorphic for now | |
130 | // and hopefully soon change this to an error. | |
131 | // | |
132 | // See #74595 for more details about this. | |
94222f64 XL |
133 | let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); |
134 | ||
5099ac24 | 135 | if concrete.is_ok() && uv.substs.has_param_types_or_consts() { |
94222f64 | 136 | match infcx.tcx.def_kind(uv.def.did) { |
3c0e092e | 137 | DefKind::AnonConst | DefKind::InlineConst => { |
94222f64 | 138 | let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); |
1b1a35ee XL |
139 | |
140 | if mir_body.is_polymorphic { | |
141 | future_compat_lint(); | |
142 | } | |
143 | } | |
144 | _ => future_compat_lint(), | |
145 | } | |
146 | } | |
147 | ||
ee023bcb FG |
148 | // If we're evaluating a foreign constant, under a nightly compiler without generic |
149 | // const exprs, AND it would've passed if that expression had been evaluated with | |
150 | // generic const exprs, then suggest using generic const exprs. | |
151 | if concrete.is_err() | |
152 | && tcx.sess.is_nightly_build() | |
153 | && !uv.def.did.is_local() | |
154 | && !tcx.features().generic_const_exprs | |
155 | && let Ok(Some(ct)) = AbstractConst::new(tcx, uv) | |
156 | && satisfied_from_param_env(tcx, ct, param_env) == Ok(true) | |
157 | { | |
158 | tcx.sess | |
159 | .struct_span_fatal( | |
160 | // Slightly better span than just using `span` alone | |
161 | if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, | |
162 | "failed to evaluate generic const expression", | |
163 | ) | |
164 | .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") | |
165 | .span_suggestion_verbose( | |
166 | rustc_span::DUMMY_SP, | |
167 | "consider enabling this feature", | |
168 | "#![feature(generic_const_exprs)]\n".to_string(), | |
169 | rustc_errors::Applicability::MaybeIncorrect, | |
170 | ) | |
171 | .emit() | |
172 | } | |
173 | ||
1b1a35ee XL |
174 | debug!(?concrete, "is_const_evaluatable"); |
175 | match concrete { | |
94222f64 | 176 | Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() { |
cdc7bbd5 XL |
177 | true => NotConstEvaluatable::MentionsInfer, |
178 | false => NotConstEvaluatable::MentionsParam, | |
179 | }), | |
180 | Err(ErrorHandled::Linted) => { | |
ee023bcb FG |
181 | let reported = |
182 | infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); | |
183 | Err(NotConstEvaluatable::Error(reported)) | |
1b1a35ee | 184 | } |
cdc7bbd5 XL |
185 | Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), |
186 | Ok(_) => Ok(()), | |
1b1a35ee XL |
187 | } |
188 | } | |
189 | ||
ee023bcb FG |
190 | #[instrument(skip(tcx), level = "debug")] |
191 | fn satisfied_from_param_env<'tcx>( | |
192 | tcx: TyCtxt<'tcx>, | |
193 | ct: AbstractConst<'tcx>, | |
194 | param_env: ty::ParamEnv<'tcx>, | |
195 | ) -> Result<bool, NotConstEvaluatable> { | |
196 | for pred in param_env.caller_bounds() { | |
197 | match pred.kind().skip_binder() { | |
198 | ty::PredicateKind::ConstEvaluatable(uv) => { | |
199 | if let Some(b_ct) = AbstractConst::new(tcx, uv)? { | |
200 | let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; | |
201 | ||
202 | // Try to unify with each subtree in the AbstractConst to allow for | |
203 | // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` | |
204 | // predicate for `(N + 1) * 2` | |
205 | let result = walk_abstract_const(tcx, b_ct, |b_ct| { | |
206 | match const_unify_ctxt.try_unify(ct, b_ct) { | |
207 | true => ControlFlow::BREAK, | |
208 | false => ControlFlow::CONTINUE, | |
209 | } | |
210 | }); | |
211 | ||
212 | if let ControlFlow::Break(()) = result { | |
213 | debug!("is_const_evaluatable: abstract_const ~~> ok"); | |
214 | return Ok(true); | |
215 | } | |
216 | } | |
217 | } | |
218 | _ => {} // don't care | |
219 | } | |
220 | } | |
221 | ||
222 | Ok(false) | |
223 | } | |
224 | ||
1b1a35ee XL |
225 | /// A tree representing an anonymous constant. |
226 | /// | |
227 | /// This is only able to represent a subset of `MIR`, | |
228 | /// and should not leak any information about desugarings. | |
29967ef6 | 229 | #[derive(Debug, Clone, Copy)] |
1b1a35ee XL |
230 | pub struct AbstractConst<'tcx> { |
231 | // FIXME: Consider adding something like `IndexSlice` | |
232 | // and use this here. | |
3c0e092e XL |
233 | inner: &'tcx [Node<'tcx>], |
234 | substs: SubstsRef<'tcx>, | |
1b1a35ee XL |
235 | } |
236 | ||
94222f64 | 237 | impl<'tcx> AbstractConst<'tcx> { |
1b1a35ee XL |
238 | pub fn new( |
239 | tcx: TyCtxt<'tcx>, | |
94222f64 | 240 | uv: ty::Unevaluated<'tcx, ()>, |
ee023bcb | 241 | ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { |
c295e0f8 | 242 | let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; |
94222f64 | 243 | debug!("AbstractConst::new({:?}) = {:?}", uv, inner); |
5099ac24 | 244 | Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs })) |
1b1a35ee XL |
245 | } |
246 | ||
29967ef6 XL |
247 | pub fn from_const( |
248 | tcx: TyCtxt<'tcx>, | |
5099ac24 | 249 | ct: ty::Const<'tcx>, |
ee023bcb | 250 | ) -> Result<Option<AbstractConst<'tcx>>, ErrorGuaranteed> { |
5099ac24 | 251 | match ct.val() { |
94222f64 | 252 | ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()), |
ee023bcb | 253 | ty::ConstKind::Error(DelaySpanBugEmitted { reported, .. }) => Err(reported), |
29967ef6 XL |
254 | _ => Ok(None), |
255 | } | |
256 | } | |
257 | ||
1b1a35ee XL |
258 | #[inline] |
259 | pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { | |
260 | AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } | |
261 | } | |
262 | ||
263 | #[inline] | |
3c0e092e XL |
264 | pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { |
265 | let node = self.inner.last().copied().unwrap(); | |
266 | match node { | |
267 | Node::Leaf(leaf) => Node::Leaf(leaf.subst(tcx, self.substs)), | |
268 | Node::Cast(kind, operand, ty) => Node::Cast(kind, operand, ty.subst(tcx, self.substs)), | |
269 | // Don't perform substitution on the following as they can't directly contain generic params | |
270 | Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, | |
271 | } | |
1b1a35ee XL |
272 | } |
273 | } | |
274 | ||
275 | struct AbstractConstBuilder<'a, 'tcx> { | |
276 | tcx: TyCtxt<'tcx>, | |
c295e0f8 XL |
277 | body_id: thir::ExprId, |
278 | body: &'a thir::Thir<'tcx>, | |
1b1a35ee | 279 | /// The current WIP node tree. |
c295e0f8 | 280 | nodes: IndexVec<NodeId, Node<'tcx>>, |
1b1a35ee XL |
281 | } |
282 | ||
283 | impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { | |
c295e0f8 XL |
284 | fn root_span(&self) -> Span { |
285 | self.body.exprs[self.body_id].span | |
286 | } | |
287 | ||
ee023bcb FG |
288 | fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { |
289 | let reported = self | |
290 | .tcx | |
c295e0f8 XL |
291 | .sess |
292 | .struct_span_err(self.root_span(), "overly complex generic constant") | |
293 | .span_label(span, msg) | |
294 | .help("consider moving this anonymous constant into a `const` function") | |
295 | .emit(); | |
296 | ||
ee023bcb | 297 | Err(reported) |
c295e0f8 | 298 | } |
ee023bcb FG |
299 | fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> { |
300 | let reported = self | |
301 | .tcx | |
1b1a35ee | 302 | .sess |
c295e0f8 XL |
303 | .struct_span_err(self.root_span(), "overly complex generic constant") |
304 | .span_label(span, msg) | |
1b1a35ee | 305 | .help("consider moving this anonymous constant into a `const` function") |
c295e0f8 | 306 | .note("this operation may be supported in the future") |
1b1a35ee XL |
307 | .emit(); |
308 | ||
ee023bcb | 309 | Err(reported) |
1b1a35ee XL |
310 | } |
311 | ||
ee023bcb | 312 | #[instrument(skip(tcx, body, body_id), level = "debug")] |
1b1a35ee XL |
313 | fn new( |
314 | tcx: TyCtxt<'tcx>, | |
c295e0f8 | 315 | (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), |
ee023bcb | 316 | ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> { |
c295e0f8 | 317 | let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; |
1b1a35ee | 318 | |
c295e0f8 XL |
319 | struct IsThirPolymorphic<'a, 'tcx> { |
320 | is_poly: bool, | |
321 | thir: &'a thir::Thir<'tcx>, | |
1b1a35ee XL |
322 | } |
323 | ||
ee023bcb | 324 | use crate::rustc_middle::thir::visit::Visitor; |
c295e0f8 | 325 | use thir::visit; |
ee023bcb FG |
326 | |
327 | impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | |
328 | fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { | |
329 | if expr.ty.has_param_types_or_consts() { | |
330 | return true; | |
331 | } | |
332 | ||
333 | match expr.kind { | |
334 | thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(), | |
335 | thir::ExprKind::ConstParam { .. } => true, | |
336 | thir::ExprKind::Repeat { value, count } => { | |
337 | self.visit_expr(&self.thir()[value]); | |
338 | count.has_param_types_or_consts() | |
339 | } | |
340 | _ => false, | |
341 | } | |
342 | } | |
343 | ||
344 | fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { | |
345 | if pat.ty.has_param_types_or_consts() { | |
346 | return true; | |
347 | } | |
348 | ||
349 | match pat.kind.as_ref() { | |
350 | thir::PatKind::Constant { value } => value.has_param_types_or_consts(), | |
351 | thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => { | |
352 | lo.has_param_types_or_consts() || hi.has_param_types_or_consts() | |
353 | } | |
354 | _ => false, | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
359 | impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { | |
c295e0f8 XL |
360 | fn thir(&self) -> &'a thir::Thir<'tcx> { |
361 | &self.thir | |
29967ef6 | 362 | } |
c295e0f8 | 363 | |
ee023bcb | 364 | #[instrument(skip(self), level = "debug")] |
c295e0f8 | 365 | fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { |
ee023bcb | 366 | self.is_poly |= self.expr_is_poly(expr); |
c295e0f8 XL |
367 | if !self.is_poly { |
368 | visit::walk_expr(self, expr) | |
369 | } | |
136023e0 | 370 | } |
29967ef6 | 371 | |
ee023bcb | 372 | #[instrument(skip(self), level = "debug")] |
c295e0f8 | 373 | fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { |
ee023bcb | 374 | self.is_poly |= self.pat_is_poly(pat); |
c295e0f8 XL |
375 | if !self.is_poly { |
376 | visit::walk_pat(self, pat); | |
377 | } | |
378 | } | |
1b1a35ee | 379 | } |
1b1a35ee | 380 | |
5099ac24 | 381 | let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; |
c295e0f8 XL |
382 | visit::walk_expr(&mut is_poly_vis, &body[body_id]); |
383 | debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); | |
384 | if !is_poly_vis.is_poly { | |
385 | return Ok(None); | |
1b1a35ee | 386 | } |
c295e0f8 XL |
387 | |
388 | Ok(Some(builder)) | |
1b1a35ee XL |
389 | } |
390 | ||
391 | /// We do not allow all binary operations in abstract consts, so filter disallowed ones. | |
392 | fn check_binop(op: mir::BinOp) -> bool { | |
393 | use mir::BinOp::*; | |
394 | match op { | |
395 | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | |
396 | | Ne | Ge | Gt => true, | |
397 | Offset => false, | |
398 | } | |
399 | } | |
400 | ||
401 | /// While we currently allow all unary operations, we still want to explicitly guard against | |
402 | /// future changes here. | |
403 | fn check_unop(op: mir::UnOp) -> bool { | |
404 | use mir::UnOp::*; | |
405 | match op { | |
406 | Not | Neg => true, | |
407 | } | |
408 | } | |
409 | ||
c295e0f8 | 410 | /// Builds the abstract const by walking the thir and bailing out when |
ee023bcb FG |
411 | /// encountering an unsupported operation. |
412 | fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { | |
c295e0f8 XL |
413 | debug!("Abstractconstbuilder::build: body={:?}", &*self.body); |
414 | self.recurse_build(self.body_id)?; | |
415 | ||
416 | for n in self.nodes.iter() { | |
5099ac24 FG |
417 | if let Node::Leaf(ty::Const(Interned( |
418 | ty::ConstS { val: ty::ConstKind::Unevaluated(ct), ty: _ }, | |
419 | _, | |
420 | ))) = n | |
421 | { | |
c295e0f8 XL |
422 | // `AbstractConst`s should not contain any promoteds as they require references which |
423 | // are not allowed. | |
424 | assert_eq!(ct.promoted, None); | |
1b1a35ee | 425 | } |
1b1a35ee | 426 | } |
c295e0f8 XL |
427 | |
428 | Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) | |
1b1a35ee XL |
429 | } |
430 | ||
ee023bcb | 431 | fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> { |
c295e0f8 XL |
432 | use thir::ExprKind; |
433 | let node = &self.body.exprs[node]; | |
c295e0f8 XL |
434 | Ok(match &node.kind { |
435 | // I dont know if handling of these 3 is correct | |
436 | &ExprKind::Scope { value, .. } => self.recurse_build(value)?, | |
437 | &ExprKind::PlaceTypeAscription { source, .. } | |
438 | | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, | |
ee023bcb FG |
439 | &ExprKind::Literal { lit, neg} => { |
440 | let sp = node.span; | |
441 | let constant = | |
442 | match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { | |
443 | Ok(c) => c, | |
444 | Err(LitToConstError::Reported) => { | |
445 | self.tcx.const_error(node.ty) | |
446 | } | |
447 | Err(LitToConstError::TypeError) => { | |
448 | bug!("encountered type error in lit_to_const") | |
449 | } | |
450 | }; | |
451 | ||
452 | self.nodes.push(Node::Leaf(constant)) | |
453 | } | |
454 | &ExprKind::NonHirLiteral { lit , user_ty: _} => { | |
455 | // FIXME Construct a Valtree from this ScalarInt when introducing Valtrees | |
456 | let const_value = ConstValue::Scalar(Scalar::Int(lit)); | |
457 | self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, const_value, node.ty))) | |
458 | } | |
459 | &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { | |
460 | let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); | |
c295e0f8 | 461 | |
ee023bcb FG |
462 | let constant = self.tcx.mk_const(ty::ConstS { |
463 | val: ty::ConstKind::Unevaluated(uneval), | |
464 | ty: node.ty, | |
465 | }); | |
466 | ||
467 | self.nodes.push(Node::Leaf(constant)) | |
468 | } | |
469 | ||
470 | ExprKind::ConstParam {param, ..} => { | |
471 | let const_param = self.tcx.mk_const(ty::ConstS { | |
472 | val: ty::ConstKind::Param(*param), | |
473 | ty: node.ty, | |
474 | }); | |
475 | self.nodes.push(Node::Leaf(const_param)) | |
476 | } | |
c295e0f8 XL |
477 | |
478 | ExprKind::Call { fun, args, .. } => { | |
479 | let fun = self.recurse_build(*fun)?; | |
480 | ||
481 | let mut new_args = Vec::<NodeId>::with_capacity(args.len()); | |
482 | for &id in args.iter() { | |
483 | new_args.push(self.recurse_build(id)?); | |
1b1a35ee | 484 | } |
c295e0f8 XL |
485 | let new_args = self.tcx.arena.alloc_slice(&new_args); |
486 | self.nodes.push(Node::FunctionCall(fun, new_args)) | |
1b1a35ee | 487 | } |
c295e0f8 XL |
488 | &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { |
489 | let lhs = self.recurse_build(lhs)?; | |
490 | let rhs = self.recurse_build(rhs)?; | |
491 | self.nodes.push(Node::Binop(op, lhs, rhs)) | |
1b1a35ee | 492 | } |
c295e0f8 XL |
493 | &ExprKind::Unary { op, arg } if Self::check_unop(op) => { |
494 | let arg = self.recurse_build(arg)?; | |
495 | self.nodes.push(Node::UnaryOp(op, arg)) | |
cdc7bbd5 | 496 | } |
c295e0f8 XL |
497 | // This is necessary so that the following compiles: |
498 | // | |
499 | // ``` | |
500 | // fn foo<const N: usize>(a: [(); N + 1]) { | |
501 | // bar::<{ N + 1 }>(); | |
502 | // } | |
503 | // ``` | |
504 | ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => { | |
505 | self.recurse_build(*e)? | |
506 | } | |
507 | // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a | |
508 | // "coercion cast" i.e. using a coercion or is a no-op. | |
509 | // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) | |
510 | &ExprKind::Use { source } => { | |
511 | let arg = self.recurse_build(source)?; | |
512 | self.nodes.push(Node::Cast(abstract_const::CastKind::Use, arg, node.ty)) | |
513 | } | |
514 | &ExprKind::Cast { source } => { | |
515 | let arg = self.recurse_build(source)?; | |
516 | self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty)) | |
1b1a35ee | 517 | } |
a2a8927a XL |
518 | ExprKind::Borrow{ arg, ..} => { |
519 | let arg_node = &self.body.exprs[*arg]; | |
520 | ||
521 | // Skip reborrows for now until we allow Deref/Borrow/AddressOf | |
522 | // expressions. | |
523 | // FIXME(generic_const_exprs): Verify/explain why this is sound | |
524 | if let ExprKind::Deref {arg} = arg_node.kind { | |
525 | self.recurse_build(arg)? | |
526 | } else { | |
527 | self.maybe_supported_error( | |
528 | node.span, | |
529 | "borrowing is not supported in generic constants", | |
530 | )? | |
531 | } | |
532 | } | |
c295e0f8 | 533 | // FIXME(generic_const_exprs): We may want to support these. |
a2a8927a | 534 | ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error( |
c295e0f8 | 535 | node.span, |
a2a8927a | 536 | "dereferencing or taking the address is not supported in generic constants", |
c295e0f8 XL |
537 | )?, |
538 | ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( | |
539 | node.span, | |
540 | "array construction is not supported in generic constants", | |
541 | )?, | |
542 | ExprKind::Block { .. } => self.maybe_supported_error( | |
543 | node.span, | |
544 | "blocks are not supported in generic constant", | |
545 | )?, | |
546 | ExprKind::NeverToAny { .. } => self.maybe_supported_error( | |
547 | node.span, | |
548 | "converting nevers to any is not supported in generic constant", | |
549 | )?, | |
550 | ExprKind::Tuple { .. } => self.maybe_supported_error( | |
551 | node.span, | |
552 | "tuple construction is not supported in generic constants", | |
553 | )?, | |
554 | ExprKind::Index { .. } => self.maybe_supported_error( | |
555 | node.span, | |
556 | "indexing is not supported in generic constant", | |
557 | )?, | |
558 | ExprKind::Field { .. } => self.maybe_supported_error( | |
559 | node.span, | |
560 | "field access is not supported in generic constant", | |
561 | )?, | |
562 | ExprKind::ConstBlock { .. } => self.maybe_supported_error( | |
563 | node.span, | |
564 | "const blocks are not supported in generic constant", | |
565 | )?, | |
566 | ExprKind::Adt(_) => self.maybe_supported_error( | |
567 | node.span, | |
568 | "struct/enum construction is not supported in generic constants", | |
569 | )?, | |
570 | // dont know if this is correct | |
571 | ExprKind::Pointer { .. } => | |
572 | self.error(node.span, "pointer casts are not allowed in generic constants")?, | |
573 | ExprKind::Yield { .. } => | |
574 | self.error(node.span, "generator control flow is not allowed in generic constants")?, | |
575 | ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self | |
576 | .error( | |
577 | node.span, | |
578 | "loops and loop control flow are not supported in generic constants", | |
579 | )?, | |
580 | ExprKind::Box { .. } => | |
581 | self.error(node.span, "allocations are not allowed in generic constants")?, | |
582 | ||
583 | ExprKind::Unary { .. } => unreachable!(), | |
584 | // we handle valid unary/binary ops above | |
585 | ExprKind::Binary { .. } => | |
586 | self.error(node.span, "unsupported binary operation in generic constants")?, | |
587 | ExprKind::LogicalOp { .. } => | |
588 | self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?, | |
589 | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { | |
590 | self.error(node.span, "assignment is not supported in generic constants")? | |
591 | } | |
592 | ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error( | |
593 | node.span, | |
594 | "closures and function keywords are not supported in generic constants", | |
595 | )?, | |
596 | // let expressions imply control flow | |
597 | ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => | |
598 | self.error(node.span, "control flow is not supported in generic constants")?, | |
5099ac24 | 599 | ExprKind::InlineAsm { .. } => { |
c295e0f8 XL |
600 | self.error(node.span, "assembly is not supported in generic constants")? |
601 | } | |
cdc7bbd5 | 602 | |
c295e0f8 XL |
603 | // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen |
604 | ExprKind::VarRef { .. } | |
605 | | ExprKind::UpvarRef { .. } | |
606 | | ExprKind::StaticRef { .. } | |
607 | | ExprKind::ThreadLocalRef(_) => { | |
608 | self.error(node.span, "unsupported operation in generic constant")? | |
609 | } | |
610 | }) | |
1b1a35ee XL |
611 | } |
612 | } | |
613 | ||
614 | /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. | |
c295e0f8 | 615 | pub(super) fn thir_abstract_const<'tcx>( |
1b1a35ee XL |
616 | tcx: TyCtxt<'tcx>, |
617 | def: ty::WithOptConstParam<LocalDefId>, | |
ee023bcb | 618 | ) -> Result<Option<&'tcx [thir::abstract_const::Node<'tcx>]>, ErrorGuaranteed> { |
94222f64 | 619 | if tcx.features().generic_const_exprs { |
1b1a35ee | 620 | match tcx.def_kind(def.did) { |
94222f64 | 621 | // FIXME(generic_const_exprs): We currently only do this for anonymous constants, |
1b1a35ee XL |
622 | // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether |
623 | // we want to look into them or treat them as opaque projections. | |
624 | // | |
625 | // Right now we do neither of that and simply always fail to unify them. | |
3c0e092e | 626 | DefKind::AnonConst | DefKind::InlineConst => (), |
1b1a35ee XL |
627 | _ => return Ok(None), |
628 | } | |
c295e0f8 | 629 | |
ee023bcb | 630 | let body = tcx.thir_body(def)?; |
c295e0f8 XL |
631 | |
632 | AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? | |
633 | .map(AbstractConstBuilder::build) | |
634 | .transpose() | |
1b1a35ee XL |
635 | } else { |
636 | Ok(None) | |
637 | } | |
638 | } | |
639 | ||
640 | pub(super) fn try_unify_abstract_consts<'tcx>( | |
641 | tcx: TyCtxt<'tcx>, | |
94222f64 | 642 | (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), |
ee023bcb | 643 | param_env: ty::ParamEnv<'tcx>, |
1b1a35ee XL |
644 | ) -> bool { |
645 | (|| { | |
94222f64 XL |
646 | if let Some(a) = AbstractConst::new(tcx, a)? { |
647 | if let Some(b) = AbstractConst::new(tcx, b)? { | |
ee023bcb FG |
648 | let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; |
649 | return Ok(const_unify_ctxt.try_unify(a, b)); | |
1b1a35ee XL |
650 | } |
651 | } | |
652 | ||
653 | Ok(false) | |
654 | })() | |
ee023bcb | 655 | .unwrap_or_else(|_: ErrorGuaranteed| true) |
94222f64 | 656 | // FIXME(generic_const_exprs): We should instead have this |
1b1a35ee | 657 | // method return the resulting `ty::Const` and return `ConstKind::Error` |
ee023bcb | 658 | // on `ErrorGuaranteed`. |
1b1a35ee XL |
659 | } |
660 | ||
ee023bcb | 661 | #[instrument(skip(tcx, f), level = "debug")] |
fc512014 | 662 | pub fn walk_abstract_const<'tcx, R, F>( |
29967ef6 XL |
663 | tcx: TyCtxt<'tcx>, |
664 | ct: AbstractConst<'tcx>, | |
665 | mut f: F, | |
fc512014 | 666 | ) -> ControlFlow<R> |
1b1a35ee | 667 | where |
5869c6ff | 668 | F: FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, |
1b1a35ee | 669 | { |
ee023bcb | 670 | #[instrument(skip(tcx, f), level = "debug")] |
fc512014 | 671 | fn recurse<'tcx, R>( |
29967ef6 XL |
672 | tcx: TyCtxt<'tcx>, |
673 | ct: AbstractConst<'tcx>, | |
5869c6ff | 674 | f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, |
fc512014 | 675 | ) -> ControlFlow<R> { |
5869c6ff | 676 | f(ct)?; |
3c0e092e | 677 | let root = ct.root(tcx); |
ee023bcb | 678 | debug!(?root); |
1b1a35ee | 679 | match root { |
29967ef6 | 680 | Node::Leaf(_) => ControlFlow::CONTINUE, |
1b1a35ee | 681 | Node::Binop(_, l, r) => { |
29967ef6 XL |
682 | recurse(tcx, ct.subtree(l), f)?; |
683 | recurse(tcx, ct.subtree(r), f) | |
1b1a35ee | 684 | } |
29967ef6 | 685 | Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f), |
1b1a35ee | 686 | Node::FunctionCall(func, args) => { |
29967ef6 XL |
687 | recurse(tcx, ct.subtree(func), f)?; |
688 | args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f)) | |
1b1a35ee | 689 | } |
136023e0 | 690 | Node::Cast(_, operand, _) => recurse(tcx, ct.subtree(operand), f), |
1b1a35ee XL |
691 | } |
692 | } | |
29967ef6 XL |
693 | |
694 | recurse(tcx, ct, &mut f) | |
1b1a35ee XL |
695 | } |
696 | ||
ee023bcb | 697 | struct ConstUnifyCtxt<'tcx> { |
1b1a35ee | 698 | tcx: TyCtxt<'tcx>, |
ee023bcb FG |
699 | param_env: ty::ParamEnv<'tcx>, |
700 | } | |
701 | ||
702 | impl<'tcx> ConstUnifyCtxt<'tcx> { | |
703 | // Substitutes generics repeatedly to allow AbstractConsts to unify where a | |
704 | // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. | |
5869c6ff | 705 | // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] |
ee023bcb FG |
706 | #[inline] |
707 | #[instrument(skip(self), level = "debug")] | |
708 | fn try_replace_substs_in_root( | |
709 | &self, | |
710 | mut abstr_const: AbstractConst<'tcx>, | |
711 | ) -> Option<AbstractConst<'tcx>> { | |
712 | while let Node::Leaf(ct) = abstr_const.root(self.tcx) { | |
713 | match AbstractConst::from_const(self.tcx, ct) { | |
714 | Ok(Some(act)) => abstr_const = act, | |
715 | Ok(None) => break, | |
716 | Err(_) => return None, | |
717 | } | |
5869c6ff | 718 | } |
5869c6ff | 719 | |
ee023bcb FG |
720 | Some(abstr_const) |
721 | } | |
29967ef6 | 722 | |
ee023bcb FG |
723 | /// Tries to unify two abstract constants using structural equality. |
724 | #[instrument(skip(self), level = "debug")] | |
725 | fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { | |
726 | let a = if let Some(a) = self.try_replace_substs_in_root(a) { | |
727 | a | |
728 | } else { | |
729 | return true; | |
730 | }; | |
731 | ||
732 | let b = if let Some(b) = self.try_replace_substs_in_root(b) { | |
733 | b | |
734 | } else { | |
735 | return true; | |
736 | }; | |
737 | ||
738 | let a_root = a.root(self.tcx); | |
739 | let b_root = b.root(self.tcx); | |
740 | debug!(?a_root, ?b_root); | |
741 | ||
742 | match (a_root, b_root) { | |
743 | (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { | |
744 | let a_ct = a_ct.eval(self.tcx, self.param_env); | |
745 | debug!("a_ct evaluated: {:?}", a_ct); | |
746 | let b_ct = b_ct.eval(self.tcx, self.param_env); | |
747 | debug!("b_ct evaluated: {:?}", b_ct); | |
748 | ||
749 | if a_ct.ty() != b_ct.ty() { | |
750 | return false; | |
1b1a35ee | 751 | } |
ee023bcb FG |
752 | |
753 | match (a_ct.val(), b_ct.val()) { | |
754 | // We can just unify errors with everything to reduce the amount of | |
755 | // emitted errors here. | |
756 | (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, | |
757 | (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { | |
758 | a_param == b_param | |
759 | } | |
760 | (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, | |
761 | // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` | |
762 | // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This | |
763 | // means that we only allow inference variables if they are equal. | |
764 | (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, | |
765 | // We expand generic anonymous constants at the start of this function, so this | |
766 | // branch should only be taking when dealing with associated constants, at | |
767 | // which point directly comparing them seems like the desired behavior. | |
768 | // | |
769 | // FIXME(generic_const_exprs): This isn't actually the case. | |
770 | // We also take this branch for concrete anonymous constants and | |
771 | // expand generic anonymous constants with concrete substs. | |
772 | (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { | |
773 | a_uv == b_uv | |
774 | } | |
775 | // FIXME(generic_const_exprs): We may want to either actually try | |
776 | // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like | |
777 | // this, for now we just return false here. | |
778 | _ => false, | |
cdc7bbd5 | 779 | } |
1b1a35ee | 780 | } |
ee023bcb FG |
781 | (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { |
782 | self.try_unify(a.subtree(al), b.subtree(bl)) | |
783 | && self.try_unify(a.subtree(ar), b.subtree(br)) | |
784 | } | |
785 | (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { | |
786 | self.try_unify(a.subtree(av), b.subtree(bv)) | |
787 | } | |
788 | (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) | |
789 | if a_args.len() == b_args.len() => | |
790 | { | |
791 | self.try_unify(a.subtree(a_f), b.subtree(b_f)) | |
792 | && iter::zip(a_args, b_args) | |
793 | .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) | |
794 | } | |
795 | (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) | |
796 | if (a_ty == b_ty) && (a_kind == b_kind) => | |
797 | { | |
798 | self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) | |
799 | } | |
800 | // use this over `_ => false` to make adding variants to `Node` less error prone | |
801 | (Node::Cast(..), _) | |
802 | | (Node::FunctionCall(..), _) | |
803 | | (Node::UnaryOp(..), _) | |
804 | | (Node::Binop(..), _) | |
805 | | (Node::Leaf(..), _) => false, | |
1b1a35ee | 806 | } |
1b1a35ee XL |
807 | } |
808 | } |