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