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