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
16fn 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
27fn 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 &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 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 { 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 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 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 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 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 ExprKind::Closure { .. } | ExprKind::Return { .. } | ExprKind::Become { .. } => {
192 error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
193 }
194 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 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
347fn 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 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}