rustc_ty_utils/
consts.rs

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