]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | use rustc_errors::ErrorGuaranteed; |
2 | use rustc_hir::def::DefKind; | |
3 | use rustc_hir::def_id::LocalDefId; | |
4 | use rustc_index::vec::IndexVec; | |
5 | use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; | |
6 | use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId}; | |
7 | use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; | |
8 | use rustc_middle::{mir, thir}; | |
9 | use rustc_span::Span; | |
10 | use rustc_target::abi::VariantIdx; | |
11 | ||
12 | use std::iter; | |
13 | ||
f2b60f7d FG |
14 | use crate::errors::{GenericConstantTooComplex, GenericConstantTooComplexSub}; |
15 | ||
064997fb FG |
16 | /// Destructures array, ADT or tuple constants into the constants |
17 | /// of their fields. | |
18 | pub(crate) fn destructure_const<'tcx>( | |
19 | tcx: TyCtxt<'tcx>, | |
20 | const_: ty::Const<'tcx>, | |
21 | ) -> ty::DestructuredConst<'tcx> { | |
22 | let ty::ConstKind::Value(valtree) = const_.kind() else { | |
23 | bug!("cannot destructure constant {:?}", const_) | |
24 | }; | |
25 | ||
26 | let branches = match valtree { | |
27 | ty::ValTree::Branch(b) => b, | |
28 | _ => bug!("cannot destructure constant {:?}", const_), | |
29 | }; | |
30 | ||
31 | let (fields, variant) = match const_.ty().kind() { | |
32 | ty::Array(inner_ty, _) | ty::Slice(inner_ty) => { | |
33 | // construct the consts for the elements of the array/slice | |
34 | let field_consts = branches | |
35 | .iter() | |
36 | .map(|b| tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty })) | |
37 | .collect::<Vec<_>>(); | |
38 | debug!(?field_consts); | |
39 | ||
40 | (field_consts, None) | |
41 | } | |
42 | ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"), | |
43 | ty::Adt(def, substs) => { | |
44 | let (variant_idx, branches) = if def.is_enum() { | |
45 | let (head, rest) = branches.split_first().unwrap(); | |
46 | (VariantIdx::from_u32(head.unwrap_leaf().try_to_u32().unwrap()), rest) | |
47 | } else { | |
48 | (VariantIdx::from_u32(0), branches) | |
49 | }; | |
50 | let fields = &def.variant(variant_idx).fields; | |
51 | let mut field_consts = Vec::with_capacity(fields.len()); | |
52 | ||
53 | for (field, field_valtree) in iter::zip(fields, branches) { | |
54 | let field_ty = field.ty(tcx, substs); | |
55 | let field_const = tcx.mk_const(ty::ConstS { | |
56 | kind: ty::ConstKind::Value(*field_valtree), | |
57 | ty: field_ty, | |
58 | }); | |
59 | field_consts.push(field_const); | |
60 | } | |
61 | debug!(?field_consts); | |
62 | ||
63 | (field_consts, Some(variant_idx)) | |
64 | } | |
65 | ty::Tuple(elem_tys) => { | |
66 | let fields = iter::zip(*elem_tys, branches) | |
67 | .map(|(elem_ty, elem_valtree)| { | |
68 | tcx.mk_const(ty::ConstS { | |
69 | kind: ty::ConstKind::Value(*elem_valtree), | |
70 | ty: elem_ty, | |
71 | }) | |
72 | }) | |
73 | .collect::<Vec<_>>(); | |
74 | ||
75 | (fields, None) | |
76 | } | |
77 | _ => bug!("cannot destructure constant {:?}", const_), | |
78 | }; | |
79 | ||
80 | let fields = tcx.arena.alloc_from_iter(fields.into_iter()); | |
81 | ||
82 | ty::DestructuredConst { variant, fields } | |
83 | } | |
84 | ||
85 | pub struct AbstractConstBuilder<'a, 'tcx> { | |
86 | tcx: TyCtxt<'tcx>, | |
87 | body_id: thir::ExprId, | |
88 | body: &'a thir::Thir<'tcx>, | |
89 | /// The current WIP node tree. | |
90 | nodes: IndexVec<NodeId, Node<'tcx>>, | |
91 | } | |
92 | ||
93 | impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { | |
94 | fn root_span(&self) -> Span { | |
95 | self.body.exprs[self.body_id].span | |
96 | } | |
97 | ||
f2b60f7d FG |
98 | fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result<!, ErrorGuaranteed> { |
99 | let reported = self.tcx.sess.emit_err(GenericConstantTooComplex { | |
100 | span: self.root_span(), | |
101 | maybe_supported: None, | |
102 | sub, | |
103 | }); | |
064997fb FG |
104 | |
105 | Err(reported) | |
106 | } | |
f2b60f7d FG |
107 | |
108 | fn maybe_supported_error( | |
109 | &mut self, | |
110 | sub: GenericConstantTooComplexSub, | |
111 | ) -> Result<!, ErrorGuaranteed> { | |
112 | let reported = self.tcx.sess.emit_err(GenericConstantTooComplex { | |
113 | span: self.root_span(), | |
114 | maybe_supported: Some(()), | |
115 | sub, | |
116 | }); | |
064997fb FG |
117 | |
118 | Err(reported) | |
119 | } | |
120 | ||
121 | #[instrument(skip(tcx, body, body_id), level = "debug")] | |
122 | pub fn new( | |
123 | tcx: TyCtxt<'tcx>, | |
124 | (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), | |
125 | ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> { | |
126 | let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() }; | |
127 | ||
128 | struct IsThirPolymorphic<'a, 'tcx> { | |
129 | is_poly: bool, | |
130 | thir: &'a thir::Thir<'tcx>, | |
131 | } | |
132 | ||
133 | use crate::rustc_middle::thir::visit::Visitor; | |
134 | use thir::visit; | |
135 | ||
136 | impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | |
137 | fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { | |
2b03887a | 138 | if expr.ty.has_non_region_param() { |
064997fb FG |
139 | return true; |
140 | } | |
141 | ||
142 | match expr.kind { | |
2b03887a | 143 | thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(), |
064997fb FG |
144 | thir::ExprKind::ConstParam { .. } => true, |
145 | thir::ExprKind::Repeat { value, count } => { | |
146 | self.visit_expr(&self.thir()[value]); | |
2b03887a | 147 | count.has_non_region_param() |
064997fb FG |
148 | } |
149 | _ => false, | |
150 | } | |
151 | } | |
152 | ||
153 | fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { | |
2b03887a | 154 | if pat.ty.has_non_region_param() { |
064997fb FG |
155 | return true; |
156 | } | |
157 | ||
f2b60f7d | 158 | match pat.kind { |
2b03887a | 159 | thir::PatKind::Constant { value } => value.has_non_region_param(), |
f2b60f7d | 160 | thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { |
2b03887a | 161 | lo.has_non_region_param() || hi.has_non_region_param() |
064997fb FG |
162 | } |
163 | _ => false, | |
164 | } | |
165 | } | |
166 | } | |
167 | ||
168 | impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { | |
169 | fn thir(&self) -> &'a thir::Thir<'tcx> { | |
170 | &self.thir | |
171 | } | |
172 | ||
173 | #[instrument(skip(self), level = "debug")] | |
174 | fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { | |
175 | self.is_poly |= self.expr_is_poly(expr); | |
176 | if !self.is_poly { | |
177 | visit::walk_expr(self, expr) | |
178 | } | |
179 | } | |
180 | ||
181 | #[instrument(skip(self), level = "debug")] | |
182 | fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { | |
183 | self.is_poly |= self.pat_is_poly(pat); | |
184 | if !self.is_poly { | |
185 | visit::walk_pat(self, pat); | |
186 | } | |
187 | } | |
188 | } | |
189 | ||
190 | let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; | |
191 | visit::walk_expr(&mut is_poly_vis, &body[body_id]); | |
192 | debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); | |
193 | if !is_poly_vis.is_poly { | |
194 | return Ok(None); | |
195 | } | |
196 | ||
197 | Ok(Some(builder)) | |
198 | } | |
199 | ||
200 | /// We do not allow all binary operations in abstract consts, so filter disallowed ones. | |
201 | fn check_binop(op: mir::BinOp) -> bool { | |
202 | use mir::BinOp::*; | |
203 | match op { | |
204 | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | |
205 | | Ne | Ge | Gt => true, | |
206 | Offset => false, | |
207 | } | |
208 | } | |
209 | ||
210 | /// While we currently allow all unary operations, we still want to explicitly guard against | |
211 | /// future changes here. | |
212 | fn check_unop(op: mir::UnOp) -> bool { | |
213 | use mir::UnOp::*; | |
214 | match op { | |
215 | Not | Neg => true, | |
216 | } | |
217 | } | |
218 | ||
219 | /// Builds the abstract const by walking the thir and bailing out when | |
220 | /// encountering an unsupported operation. | |
221 | pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { | |
222 | debug!("AbstractConstBuilder::build: body={:?}", &*self.body); | |
223 | self.recurse_build(self.body_id)?; | |
224 | ||
064997fb FG |
225 | Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter())) |
226 | } | |
227 | ||
228 | fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> { | |
229 | use thir::ExprKind; | |
230 | let node = &self.body.exprs[node]; | |
231 | Ok(match &node.kind { | |
232 | // I dont know if handling of these 3 is correct | |
233 | &ExprKind::Scope { value, .. } => self.recurse_build(value)?, | |
234 | &ExprKind::PlaceTypeAscription { source, .. } | |
235 | | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, | |
f2b60f7d | 236 | &ExprKind::Literal { lit, neg } => { |
064997fb | 237 | let sp = node.span; |
f2b60f7d FG |
238 | let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput { |
239 | lit: &lit.node, | |
240 | ty: node.ty, | |
241 | neg, | |
242 | }) { | |
243 | Ok(c) => c, | |
244 | Err(LitToConstError::Reported) => self.tcx.const_error(node.ty), | |
245 | Err(LitToConstError::TypeError) => { | |
246 | bug!("encountered type error in lit_to_const") | |
247 | } | |
248 | }; | |
064997fb FG |
249 | |
250 | self.nodes.push(Node::Leaf(constant)) | |
251 | } | |
f2b60f7d | 252 | &ExprKind::NonHirLiteral { lit, user_ty: _ } => { |
064997fb FG |
253 | let val = ty::ValTree::from_scalar_int(lit); |
254 | self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) | |
255 | } | |
256 | &ExprKind::ZstLiteral { user_ty: _ } => { | |
257 | let val = ty::ValTree::zst(); | |
258 | self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty))) | |
259 | } | |
260 | &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { | |
2b03887a FG |
261 | let uneval = |
262 | ty::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs); | |
064997fb | 263 | |
f2b60f7d FG |
264 | let constant = self |
265 | .tcx | |
266 | .mk_const(ty::ConstS { kind: ty::ConstKind::Unevaluated(uneval), ty: node.ty }); | |
064997fb FG |
267 | |
268 | self.nodes.push(Node::Leaf(constant)) | |
269 | } | |
270 | ||
f2b60f7d FG |
271 | ExprKind::ConstParam { param, .. } => { |
272 | let const_param = self | |
273 | .tcx | |
274 | .mk_const(ty::ConstS { kind: ty::ConstKind::Param(*param), ty: node.ty }); | |
064997fb FG |
275 | self.nodes.push(Node::Leaf(const_param)) |
276 | } | |
277 | ||
278 | ExprKind::Call { fun, args, .. } => { | |
279 | let fun = self.recurse_build(*fun)?; | |
280 | ||
281 | let mut new_args = Vec::<NodeId>::with_capacity(args.len()); | |
282 | for &id in args.iter() { | |
283 | new_args.push(self.recurse_build(id)?); | |
284 | } | |
285 | let new_args = self.tcx.arena.alloc_slice(&new_args); | |
286 | self.nodes.push(Node::FunctionCall(fun, new_args)) | |
287 | } | |
288 | &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => { | |
289 | let lhs = self.recurse_build(lhs)?; | |
290 | let rhs = self.recurse_build(rhs)?; | |
291 | self.nodes.push(Node::Binop(op, lhs, rhs)) | |
292 | } | |
293 | &ExprKind::Unary { op, arg } if Self::check_unop(op) => { | |
294 | let arg = self.recurse_build(arg)?; | |
295 | self.nodes.push(Node::UnaryOp(op, arg)) | |
296 | } | |
297 | // This is necessary so that the following compiles: | |
298 | // | |
299 | // ``` | |
300 | // fn foo<const N: usize>(a: [(); N + 1]) { | |
301 | // bar::<{ N + 1 }>(); | |
302 | // } | |
303 | // ``` | |
f2b60f7d FG |
304 | ExprKind::Block { block } => { |
305 | if let thir::Block { stmts: box [], expr: Some(e), .. } = &self.body.blocks[*block] | |
306 | { | |
307 | self.recurse_build(*e)? | |
308 | } else { | |
309 | self.maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported( | |
310 | node.span, | |
311 | ))? | |
312 | } | |
064997fb FG |
313 | } |
314 | // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a | |
315 | // "coercion cast" i.e. using a coercion or is a no-op. | |
316 | // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested) | |
317 | &ExprKind::Use { source } => { | |
318 | let arg = self.recurse_build(source)?; | |
319 | self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty)) | |
320 | } | |
321 | &ExprKind::Cast { source } => { | |
322 | let arg = self.recurse_build(source)?; | |
323 | self.nodes.push(Node::Cast(CastKind::As, arg, node.ty)) | |
324 | } | |
f2b60f7d | 325 | ExprKind::Borrow { arg, .. } => { |
064997fb FG |
326 | let arg_node = &self.body.exprs[*arg]; |
327 | ||
328 | // Skip reborrows for now until we allow Deref/Borrow/AddressOf | |
329 | // expressions. | |
330 | // FIXME(generic_const_exprs): Verify/explain why this is sound | |
331 | if let ExprKind::Deref { arg } = arg_node.kind { | |
332 | self.recurse_build(arg)? | |
333 | } else { | |
f2b60f7d | 334 | self.maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported( |
064997fb | 335 | node.span, |
f2b60f7d | 336 | ))? |
064997fb FG |
337 | } |
338 | } | |
339 | // FIXME(generic_const_exprs): We may want to support these. | |
f2b60f7d FG |
340 | ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => self.maybe_supported_error( |
341 | GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span), | |
064997fb | 342 | )?, |
f2b60f7d FG |
343 | ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error( |
344 | GenericConstantTooComplexSub::ArrayNotSupported(node.span), | |
064997fb FG |
345 | )?, |
346 | ExprKind::NeverToAny { .. } => self.maybe_supported_error( | |
f2b60f7d | 347 | GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span), |
064997fb FG |
348 | )?, |
349 | ExprKind::Tuple { .. } => self.maybe_supported_error( | |
f2b60f7d | 350 | GenericConstantTooComplexSub::TupleNotSupported(node.span), |
064997fb FG |
351 | )?, |
352 | ExprKind::Index { .. } => self.maybe_supported_error( | |
f2b60f7d | 353 | GenericConstantTooComplexSub::IndexNotSupported(node.span), |
064997fb FG |
354 | )?, |
355 | ExprKind::Field { .. } => self.maybe_supported_error( | |
f2b60f7d | 356 | GenericConstantTooComplexSub::FieldNotSupported(node.span), |
064997fb FG |
357 | )?, |
358 | ExprKind::ConstBlock { .. } => self.maybe_supported_error( | |
f2b60f7d | 359 | GenericConstantTooComplexSub::ConstBlockNotSupported(node.span), |
064997fb | 360 | )?, |
f2b60f7d FG |
361 | ExprKind::Adt(_) => self |
362 | .maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?, | |
064997fb | 363 | // dont know if this is correct |
f2b60f7d FG |
364 | ExprKind::Pointer { .. } => { |
365 | self.error(GenericConstantTooComplexSub::PointerNotSupported(node.span))? | |
366 | } | |
367 | ExprKind::Yield { .. } => { | |
368 | self.error(GenericConstantTooComplexSub::YieldNotSupported(node.span))? | |
369 | } | |
370 | ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => { | |
371 | self.error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? | |
372 | } | |
373 | ExprKind::Box { .. } => { | |
374 | self.error(GenericConstantTooComplexSub::BoxNotSupported(node.span))? | |
375 | } | |
064997fb FG |
376 | |
377 | ExprKind::Unary { .. } => unreachable!(), | |
378 | // we handle valid unary/binary ops above | |
f2b60f7d FG |
379 | ExprKind::Binary { .. } => { |
380 | self.error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))? | |
381 | } | |
382 | ExprKind::LogicalOp { .. } => { | |
383 | self.error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))? | |
384 | } | |
064997fb | 385 | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { |
f2b60f7d FG |
386 | self.error(GenericConstantTooComplexSub::AssignNotSupported(node.span))? |
387 | } | |
388 | ExprKind::Closure { .. } | ExprKind::Return { .. } => { | |
389 | self.error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))? | |
064997fb | 390 | } |
064997fb | 391 | // let expressions imply control flow |
f2b60f7d FG |
392 | ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => { |
393 | self.error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))? | |
394 | } | |
064997fb | 395 | ExprKind::InlineAsm { .. } => { |
f2b60f7d | 396 | self.error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? |
064997fb FG |
397 | } |
398 | ||
399 | // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen | |
400 | ExprKind::VarRef { .. } | |
401 | | ExprKind::UpvarRef { .. } | |
402 | | ExprKind::StaticRef { .. } | |
403 | | ExprKind::ThreadLocalRef(_) => { | |
f2b60f7d | 404 | self.error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? |
064997fb FG |
405 | } |
406 | }) | |
407 | } | |
408 | } | |
409 | ||
410 | /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. | |
411 | pub fn thir_abstract_const<'tcx>( | |
412 | tcx: TyCtxt<'tcx>, | |
413 | def: ty::WithOptConstParam<LocalDefId>, | |
414 | ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> { | |
415 | if tcx.features().generic_const_exprs { | |
416 | match tcx.def_kind(def.did) { | |
417 | // FIXME(generic_const_exprs): We currently only do this for anonymous constants, | |
418 | // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether | |
419 | // we want to look into them or treat them as opaque projections. | |
420 | // | |
421 | // Right now we do neither of that and simply always fail to unify them. | |
422 | DefKind::AnonConst | DefKind::InlineConst => (), | |
423 | _ => return Ok(None), | |
424 | } | |
425 | ||
426 | let body = tcx.thir_body(def)?; | |
427 | ||
428 | AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))? | |
429 | .map(AbstractConstBuilder::build) | |
430 | .transpose() | |
431 | } else { | |
432 | Ok(None) | |
433 | } | |
434 | } | |
435 | ||
436 | pub fn provide(providers: &mut ty::query::Providers) { | |
437 | *providers = ty::query::Providers { | |
438 | destructure_const, | |
439 | thir_abstract_const: |tcx, def_id| { | |
440 | let def_id = def_id.expect_local(); | |
441 | if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { | |
442 | tcx.thir_abstract_const_of_const_arg(def) | |
443 | } else { | |
444 | thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id)) | |
445 | } | |
446 | }, | |
447 | thir_abstract_const_of_const_arg: |tcx, (did, param_did)| { | |
448 | thir_abstract_const( | |
449 | tcx, | |
450 | ty::WithOptConstParam { did, const_param_did: Some(param_did) }, | |
451 | ) | |
452 | }, | |
453 | ..*providers | |
454 | }; | |
455 | } |