rustc_ty_utils/
consts.rs

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