]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_ty_utils/src/consts.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_ty_utils / src / consts.rs
1 use rustc_errors::ErrorGuaranteed;
2 use rustc_hir::def::DefKind;
3 use rustc_hir::def_id::LocalDefId;
4 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
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};
9 use rustc_middle::{mir, thir};
10 use rustc_span::Span;
11 use rustc_target::abi::VariantIdx;
12
13 use std::iter;
14
15 use crate::errors::{GenericConstantTooComplex, GenericConstantTooComplexSub};
16
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
35 let field_consts =
36 branches.iter().map(|b| tcx.mk_const(*b, *inner_ty)).collect::<Vec<_>>();
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);
54 let field_const = tcx.mk_const(*field_valtree, field_ty);
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)
63 .map(|(elem_ty, elem_valtree)| tcx.mk_const(*elem_valtree, elem_ty))
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
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,
83 }
84 }
85
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,
92 }
93 }
94
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)?
113 }
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)
120 }
121 Err(LitToConstError::TypeError) => {
122 bug!("encountered type error in lit_to_const")
123 }
124 }
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),
139
140 ExprKind::Call { fun, args, .. } => {
141 let fun = recurse_build(tcx, body, *fun, root_span)?;
142
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)?);
146 }
147 let new_args = tcx.mk_const_list(new_args.iter());
148 tcx.mk_const(Expr::FunctionCall(fun, new_args), node.ty)
149 }
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))?
171 }
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))?
194 }
195 }
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))?
202 }
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))?,
232
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))?
253 }
254
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))?
261 }
262 })
263 }
264
265 struct IsThirPolymorphic<'a, 'tcx> {
266 is_poly: bool,
267 thir: &'a thir::Thir<'tcx>,
268 }
269
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 }
283
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 }
297
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 }
303
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()
310 }
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 }
318
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()
323 }
324 _ => false,
325 }
326 }
327 }
328
329 impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
330 fn thir(&self) -> &'a thir::Thir<'tcx> {
331 &self.thir
332 }
333
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 }
341
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 }
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>,
355 ) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
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)?;
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;
377
378 Some(recurse_build(tcx, body, body_id, root_span)).transpose()
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 }